Linked list

This commit is contained in:
2022-11-11 00:12:28 +01:00
parent a3616bd701
commit 262dca3a4d
13 changed files with 16453 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
!.meta
# Protected or generated
.git
.vscode
# When using npm
node_modules/*
# Configuration files
.eslintrc.cjs
babel.config.cjs
jest.config.cjs

View File

@@ -0,0 +1,41 @@
module.exports = {
root: true,
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
overrides: [
// Student provided files
{
files: ['*.ts'],
excludedFiles: ['.meta/proof.ci.ts', '.meta/exemplar.ts', '*.test.ts'],
extends: '@exercism/eslint-config-typescript',
},
// Exercism given tests
{
files: ['*.test.ts'],
excludedFiles: ['custom.test.ts'],
env: {
jest: true,
},
extends: '@exercism/eslint-config-typescript/maintainers',
},
// Student provided tests
{
files: ['custom.test.ts'],
env: {
jest: true,
},
extends: '@exercism/eslint-config-typescript',
},
// Exercism provided files
{
files: ['.meta/proof.ci.ts', '.meta/exemplar.ts', '*.test.ts'],
excludedFiles: ['custom.test.ts'],
extends: '@exercism/eslint-config-typescript/maintainers',
rules: {
"@typescript-eslint/semi": 0,
},
},
],
}

View File

@@ -0,0 +1,11 @@
{
"blurb": "Implement a doubly linked list",
"authors": ["jspengeman"],
"contributors": ["masters3d", "SleeplessByte"],
"files": {
"solution": ["linked-list.ts"],
"test": ["linked-list.test.ts"],
"example": [".meta/proof.ci.ts"]
},
"source": "Classic computer science topic"
}

View File

@@ -0,0 +1 @@
{"track":"typescript","exercise":"linked-list","id":"3f131a779e684267985af3d611b37402","url":"https://exercism.org/tracks/typescript/exercises/linked-list","handle":"briemens","is_requester":true,"auto_approve":false}

View File

@@ -0,0 +1,44 @@
# Help
## Running the tests
Execute the tests with:
```bash
$ yarn test
```
## Skipped tests
In the test suites all tests but the first have been skipped.
Once you get a test passing, you can enable the next one by changing `xit` to
`it`.
## Submitting your solution
You can submit your solution using the `exercism submit linked-list.ts` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [TypeScript track's documentation](https://exercism.org/docs/tracks/typescript)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [TypeScript QuickStart](https://www.typescriptlang.org/docs/handbook/release-notes/overview.html)
- [ECMAScript 2015 Language Specification](https://www.ecma-international.org/wp-content/uploads/ECMA-262_6th_edition_june_2015.pdf) (pdf)
- [Mozilla JavaScript Reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference)
- [/r/typescript](https://www.reddit.com/r/typescript) is the TypeScript subreddit.
- [StackOverflow](https://stackoverflow.com/questions/tagged/typescript) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.

View File

@@ -0,0 +1,48 @@
# Linked List
Welcome to Linked List on Exercism's TypeScript Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Implement a doubly linked list.
Like an array, a linked list is a simple linear data structure. Several
common data types can be implemented using linked lists, like queues,
stacks, and associative arrays.
A linked list is a collection of data elements called _nodes_. In a
_singly linked list_ each node holds a value and a link to the next node.
In a _doubly linked list_ each node also holds a link to the previous
node.
You will write an implementation of a doubly linked list. Implement a
Node to hold a value and pointers to the next and previous nodes. Then
implement a List which holds references to the first and last node and
offers an array-like interface for adding and removing items:
- `push` (_insert value at back_);
- `pop` (_remove value at back_);
- `shift` (_remove value at front_).
- `unshift` (_insert value at front_);
To keep your implementation simple, the tests will not cover error
conditions. Specifically: `pop` or `shift` will never be called on an
empty list.
If you want to know more about linked lists, check [Wikipedia](https://en.wikipedia.org/wiki/Linked_list).
## Source
### Created by
- @jspengeman
### Contributed to by
- @masters3d
- @SleeplessByte
### Based on
Classic computer science topic

View File

@@ -0,0 +1,4 @@
module.exports = {
presets: ['@exercism/babel-preset-typescript'],
plugins: [],
}

View File

@@ -0,0 +1,19 @@
module.exports = {
verbose: true,
projects: ['<rootDir>'],
testMatch: [
'**/__tests__/**/*.[jt]s?(x)',
'**/test/**/*.[jt]s?(x)',
'**/?(*.)+(spec|test).[jt]s?(x)',
],
testPathIgnorePatterns: [
'/(?:production_)?node_modules/',
'.d.ts$',
'<rootDir>/test/fixtures',
'<rootDir>/test/helpers',
'__mocks__',
],
transform: {
'^.+\\.[jt]sx?$': 'babel-jest',
},
}

View File

@@ -0,0 +1,101 @@
import { LinkedList } from "./linked-list";
describe("LinkedList", () => {
it("add/extract elements to the end of the list with push/pop", () => {
const list = new LinkedList<number>();
list.push(10);
list.push(20);
expect(list.pop()).toBe(20);
expect(list.pop()).toBe(10);
});
it("extract elements from the beginning of the list with shift", () => {
const list = new LinkedList<number>();
list.push(10);
list.push(20);
expect(list.shift()).toBe(10);
expect(list.shift()).toBe(20);
});
it("add/extract elements from the beginning of the list with unshift/shift", () => {
const list = new LinkedList<number>();
list.unshift(10);
list.unshift(20);
expect(list.shift()).toBe(20);
expect(list.shift()).toBe(10);
});
it("unshift/pop", () => {
const list = new LinkedList<number>();
list.unshift(10);
list.unshift(20);
expect(list.pop()).toBe(10);
expect(list.pop()).toBe(20);
});
it("example", () => {
const list = new LinkedList<number>();
list.push(10);
list.push(20);
expect(list.pop()).toBe(20);
list.push(30);
expect(list.shift()).toBe(10);
list.unshift(40);
list.push(50);
expect(list.shift()).toBe(40);
expect(list.pop()).toBe(50);
expect(list.shift()).toBe(30);
});
it("can count its elements", () => {
const list = new LinkedList<number>();
expect(list.count()).toBe(0);
list.push(10);
expect(list.count()).toBe(1);
list.push(20);
expect(list.count()).toBe(2);
});
it("sets head/tail after popping last element", () => {
const list = new LinkedList<number>();
list.push(10);
list.pop();
list.unshift(20);
expect(list.count()).toBe(1);
expect(list.pop()).toBe(20);
});
it("sets head/tail after shifting last element", () => {
const list = new LinkedList<number>();
list.unshift(10);
list.shift();
list.push(20);
expect(list.count()).toBe(1);
expect(list.shift()).toBe(20);
});
it("deletes the element with the specified value from the list", () => {
const list = new LinkedList<number>();
list.push(10);
list.push(20);
list.push(30);
list.delete(20);
expect(list.count()).toBe(2);
expect(list.pop()).toBe(30);
expect(list.shift()).toBe(10);
});
it("deletes the only element", () => {
const list = new LinkedList<number>();
list.push(10);
list.delete(10);
expect(list.count()).toBe(0);
});
it("delete does not modify the list if the element is not found", () => {
const list = new LinkedList<number>();
list.push(10);
list.delete(20);
expect(list.count()).toBe(1);
});
});

View File

@@ -0,0 +1,116 @@
interface Node<TValue> {
previous?: Node<TValue>;
value: TValue;
next?: Node<TValue>;
}
const push = <TValue extends any>(
node: Node<TValue> | undefined,
value: TValue
): Node<TValue> => {
if (!node) {
return { value };
}
if (node.next) {
push(node.next, value);
return node;
}
node.next = { previous: node, value };
return node;
};
const pop = <TValue extends any>(
node?: Node<TValue>
): { node: Node<TValue> | undefined; value: TValue | undefined } => {
if (!node) {
return { node: undefined, value: undefined };
}
if (node.next) {
const { value } = pop(node.next);
return { node, value };
}
if (node.previous) {
node.previous.next = undefined;
}
return { node: undefined, value: node.value };
};
const shift = <TValue extends any>(
node?: Node<TValue>
): { node: Node<TValue> | undefined; value: TValue | undefined } => {
if (!node) {
return { node: undefined, value: undefined };
}
if (node.next) {
node.next.previous = undefined;
return { node: node.next, value: node.value };
}
return { node: undefined, value: node.value };
};
const unshift = <TValue extends any>(
node: Node<TValue> | undefined,
value: TValue
): Node<TValue> => {
if (!node) {
return { value };
}
const root = { value, next: node };
node.previous = root;
return root;
};
const count = <TValue extends any>(node: Node<TValue> | undefined): number => {
if (!node) {
return 0;
}
return 1 + count(node.next);
};
const del = <TValue extends any>(
node: Node<TValue> | undefined,
value: TValue
): Node<TValue> | undefined => {
if (!node) {
return undefined;
}
if (node.next) {
del(node.next, value);
}
if (node.value === value) {
if (node.previous) {
node.previous.next = node.next;
}
if (node.next) {
node.next.previous = node.previous;
}
return undefined;
}
return node;
};
export class LinkedList<TElement> {
public node: Node<TElement> | undefined;
push(value: TElement): void {
this.node = push(this.node, value);
}
pop(): TElement | undefined {
const { node, value } = pop(this.node);
this.node = node;
return value;
}
shift(): TElement | undefined {
const { node, value } = shift(this.node);
this.node = node;
return value;
}
unshift(value: TElement): void {
this.node = unshift(this.node, value);
}
delete(element: TElement): void {
this.node = del(this.node, element);
}
count(): number {
return count(this.node);
}
}

15996
typescript/linked-list/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
{
"name": "@exercism/typescript-linked-list",
"version": "1.0.0",
"description": "Exercism exercises in Typescript.",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/exercism/typescript"
},
"type": "module",
"engines": {
"node": "^14.13.1 || >=16.0.0"
},
"devDependencies": {
"@exercism/babel-preset-typescript": "^0.1.0",
"@exercism/eslint-config-typescript": "^0.4.1",
"@types/jest": "^27.4.0",
"@types/node": "^16.11.24",
"babel-jest": "^27.5.1",
"core-js": "^3.21.0",
"eslint": "^8.9.0",
"jest": "^27.5.1",
"typescript": "^4.5.4"
},
"scripts": {
"test": "yarn lint:types && jest --no-cache",
"lint": "yarn lint:types && yarn lint:ci",
"lint:types": "yarn tsc --noEmit -p .",
"lint:ci": "eslint . --ext .tsx,.ts"
}
}

View File

@@ -0,0 +1,28 @@
{
"display": "Configuration for Exercism TypeScript Exercises",
"compilerOptions": {
// Allows you to use the newest syntax, and have access to console.log
// https://www.typescriptlang.org/tsconfig#lib
"lib": ["ESNEXT", "dom"],
// Make sure typescript is configured to output ESM
// https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-make-my-typescript-project-output-esm
"module": "ES2020",
// Since this project is using babel, TypeScript may target something very
// high, and babel will make sure it runs on your local Node version.
// https://babeljs.io/docs/en/
"target": "ESNext", // ESLint doesn't support this yet: "es2022",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
// Because we'll be using babel: ensure that Babel can safely transpile
// files in the TypeScript project.
//
// https://babeljs.io/docs/en/babel-plugin-transform-typescript/#caveats
"isolatedModules": true
},
"include": ["*.ts", "*.tsx", ".meta/*.ts", ".meta/*.tsx"],
"exclude": ["node_modules"]
}