Skip to content

Commit 2434779

Browse files
🌱 initial: First implementation based on existing circular.
1 parent 43bc831 commit 2434779

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+9947
-18
lines changed

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
:left_right_arrow: [@data-structure-algebra/doubly-linked-list](https://data-structure-algebra.github.io/doubly-linked-list)
22
==
33

4-
Doubly linked list for JavaScript.
4+
Doubly linked lists for JavaScript.
55
See [docs](https://data-structure-algebra.github.io/doubly-linked-list/index.html).
6-
7-
> :building_construction: Caveat emptor! This is work in progress. Code may be
8-
> working. Documentation may be present. Coherence may be. Maybe.
6+
Parent is [js-data-structures](https://github.com/make-github-pseudonymous-again/js-data-structures).
97

108
> :warning: Depending on your environment, the code may require
119
> `regeneratorRuntime` to be defined, for instance by importing
1210
> [regenerator-runtime/runtime](https://www.npmjs.com/package/regenerator-runtime).
1311
12+
```js
13+
import {from} from '@data-structure-algebra/doubly-linked-list';
14+
let list = from('abc');
15+
```
16+
1417
[![License](https://img.shields.io/github/license/data-structure-algebra/doubly-linked-list.svg)](https://raw.githubusercontent.com/data-structure-algebra/doubly-linked-list/main/LICENSE)
1518
[![Version](https://img.shields.io/npm/v/@data-structure-algebra/doubly-linked-list.svg)](https://www.npmjs.org/package/@data-structure-algebra/doubly-linked-list)
1619
[![Tests](https://img.shields.io/github/workflow/status/data-structure-algebra/doubly-linked-list/ci?event=push&label=tests)](https://github.com/data-structure-algebra/doubly-linked-list/actions/workflows/ci.yml?query=branch:main)

doc/scripts/header.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ domReady(() => {
1717
header.insertBefore(projectname, header.firstChild);
1818

1919
const testlink = document.querySelector('header > a[data-ice="testLink"]');
20-
testlink.href = 'https://app.codecov.io/gh/data-structure-algebra/doubly-linked-list';
20+
testlink.href =
21+
'https://app.codecov.io/gh/data-structure-algebra/doubly-linked-list';
2122
testlink.target = '_BLANK';
2223

2324
const searchBox = document.querySelector('.search-box');

package.json

+6-10
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"test:dist": "npm run test:modern && npm run test:module && npm run test:cjs",
6767
"test:modern": "IMPORT_MAP_PATH=test/import-maps/dist/index.modern.json npm run test-cmd",
6868
"test:module": "IMPORT_MAP_PATH=test/import-maps/dist/index.module.json npm run test-cmd",
69-
"test:src": "IMPORT_MAP_PATH=test/import-maps/src/index.json npm run test-cmd"
69+
"test:src": "IMPORT_MAP_PATH=test/import-maps/src/index.json npm run test-cmd --"
7070
},
7171
"dependencies": {},
7272
"devDependencies": {
@@ -75,6 +75,9 @@
7575
"@babel/plugin-transform-for-of": "7.18.8",
7676
"@babel/preset-env": "7.19.4",
7777
"@commitlint/cli": "17.1.2",
78+
"@iterable-iterator/list": "^1.0.1",
79+
"@iterable-iterator/map": "^1.0.1",
80+
"@iterable-iterator/range": "^2.1.0",
7881
"@js-library/commitlint-config": "0.0.4",
7982
"@node-loader/babel": "2.0.1",
8083
"@node-loader/core": "2.0.0",
@@ -208,16 +211,9 @@
208211
"unicorn"
209212
],
210213
"rules": {
214+
"camelcase": "off",
211215
"unicorn/prefer-node-protocol": "off",
212-
"unicorn/filename-case": [
213-
"error",
214-
{
215-
"cases": {
216-
"camelCase": true,
217-
"pascalCase": true
218-
}
219-
}
220-
]
216+
"unicorn/filename-case": "off"
221217
},
222218
"overrides": [
223219
{

src/Node.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Base node class.
3+
*
4+
* @class
5+
* @param {any} value The value to hold.
6+
*/
7+
export default function Node(value) {
8+
/** @member {any} The value/key held by this node. */
9+
this.value = value;
10+
/** @member {Node} Pointer to previous (left) sibling */
11+
this.prev = null;
12+
/** @member {Node} Pointer to next (right) sibling */
13+
this.next = null;
14+
}

src/_concat.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
4+
/**
5+
* Concatenate two input lists.
6+
*
7+
* @param {Node} z Last node of first input list.
8+
* @param {Node} y First node of second input list.
9+
*/
10+
export default function _concat(z, y) {
11+
assert(z instanceof Node && z.next === null);
12+
assert(y instanceof Node && y.prev === null);
13+
z.next = y;
14+
y.prev = z;
15+
}

src/_iter.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
4+
/**
5+
* Generator of nodes in list in order. You are allowed to edit the current
6+
* node.
7+
*
8+
* /!\ Modifying the next pointer of the current node will NOT change which
9+
* node comes next in the iteration.
10+
*
11+
* @param {Node} first First node of the list.
12+
* @return {IterableIterator<Node>} Yields nodes of a list in order.
13+
*/
14+
export default function* _iter(first) {
15+
assert(first instanceof Node);
16+
let next = first;
17+
18+
do {
19+
const x = next;
20+
next = x.next; // Compute next before yielding.
21+
yield x; // Necessary ?
22+
} while (next !== null);
23+
}

src/_iter_fast.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
4+
/**
5+
* Generator of nodes in list in order. The list cannot be empty. You should
6+
* not modify the current node's next pointer unless you know what you are
7+
* doing.
8+
*
9+
* /!\ Modifying the next pointer of the current node will change which node
10+
* comes next in the iteration.
11+
*
12+
* @param {Node} first First node of the list.
13+
* @return {IterableIterator<Node>} Yields nodes of a list in order.
14+
*/
15+
export default function* _iter_fast(first) {
16+
assert(first instanceof Node);
17+
let next = first;
18+
19+
do {
20+
yield next;
21+
next = next.next;
22+
} while (next !== null);
23+
}

src/_last.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
4+
/**
5+
* Returns the last node of a list. The list cannot be empty.
6+
*
7+
* @param {Node} first First node of the list.
8+
* @return {Node} Last node of the list.
9+
*/
10+
export default function _last(first) {
11+
assert(first instanceof Node);
12+
let next = first;
13+
14+
while (next.next !== null) {
15+
next = next.next;
16+
}
17+
18+
return next;
19+
}

src/_len.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
4+
/**
5+
* Compute the length of a non-empty list.
6+
*
7+
* @param {Node} x First node of the input list.
8+
* @return {number} The length of the input list.
9+
*/
10+
// eslint-disable-next-line unicorn/prevent-abbreviations
11+
export default function _len(x) {
12+
assert(x instanceof Node);
13+
let n = 1;
14+
let y = x.next;
15+
while (y !== null) {
16+
++n;
17+
y = y.next;
18+
}
19+
20+
return n;
21+
}

src/_pop.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
4+
/**
5+
* Removes last {@link Node} from a non-empty list.
6+
*
7+
* @param {Node} x Last node (not null). Cannot be the first node.
8+
* @return {Node} Last node of new list (not null).
9+
*/
10+
export default function _pop(x) {
11+
assert(x instanceof Node);
12+
assert(x.next === null);
13+
const prev = x.prev; // eslint-disable-line unicorn/prevent-abbreviations
14+
assert(prev !== null);
15+
prev.next = null;
16+
return prev;
17+
}

src/_remove.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
4+
/**
5+
* Removes input {@link Node} from its list. Cannot be first or last, use
6+
* _shift or _pop instead.
7+
*
8+
* /!\ Pointers in the extracted node are left unchanged.
9+
* /!\ <code>x</code> will have dangling pointers after removal if not single element.
10+
*
11+
* @param {Node} x Node to remove.
12+
*/
13+
export default function _remove(x) {
14+
assert(x instanceof Node);
15+
x.prev.next = x.next;
16+
x.next.prev = x.prev;
17+
}

src/_rotate_left.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
import _rotate_to from './_rotate_to.js';
4+
5+
/**
6+
* Rotate list to the left k steps. The parameter k must be positive and
7+
* smaller than the length of the list.
8+
*
9+
* @param {Node} x The current first node.
10+
* @param {Node} z The current last node.
11+
* @param {number} k MUST be positive and smaller than the length of the list.
12+
* @return {[Node, Node]} The new first and last nodes.
13+
*/
14+
export default function _rotate_left(x, z, k) {
15+
assert(Number.isInteger(k));
16+
assert(k > 0);
17+
assert(x instanceof Node);
18+
assert(x.prev === null);
19+
assert(z instanceof Node);
20+
assert(z.next === null);
21+
assert(x !== z);
22+
let y = z;
23+
while (--k) {
24+
y = y.prev;
25+
assert(y instanceof Node);
26+
}
27+
28+
const w = y.prev;
29+
_rotate_to(x, y, z);
30+
return [y, w];
31+
}

src/_rotate_left_modulo.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
import _rotate_left from './_rotate_left.js';
4+
5+
/**
6+
* Rotate non-empty list to the left n steps. The parameter n must be positive.
7+
*
8+
* @param {Node} x The current first node.
9+
* @param {Node} z The current last node.
10+
* @param {number} n List length, MUST be positive.
11+
* @param {number} k MUST be positive.
12+
* @return {[Node, Node]} The new first and last nodes.
13+
*/
14+
export default function _rotate_left_modulo(x, z, n, k) {
15+
assert(Number.isInteger(n));
16+
assert(n > 0);
17+
assert(Number.isInteger(k));
18+
assert(k > 0);
19+
assert(x instanceof Node);
20+
assert(x.prev === null);
21+
assert(z instanceof Node);
22+
assert(z.next === null);
23+
assert(x !== z);
24+
const m = k % n;
25+
return m === 0 ? [x, z] : _rotate_left(x, z, m);
26+
}

src/_rotate_left_unknown_length.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
import _rotate_to from './_rotate_to.js';
4+
import _rotate_left_modulo from './_rotate_left_modulo.js';
5+
6+
/**
7+
* Rotate list to the right n steps. The parameter n must be positive.
8+
*
9+
* @param {Node} x The current first node.
10+
* @param {Node} z The current last node.
11+
* @param {number} k MUST be positive.
12+
* @return {[Node, Node]} The new first and last nodes.
13+
*/
14+
export default function _rotate_left_unknown_length(x, z, k) {
15+
assert(Number.isInteger(k));
16+
assert(k > 0);
17+
assert(x instanceof Node);
18+
assert(x.prev === null);
19+
assert(z instanceof Node);
20+
assert(z.next === null);
21+
assert(x !== z);
22+
let y = z;
23+
let l = k;
24+
while (--l) {
25+
if (y === x) {
26+
return _rotate_left_modulo(x, z, k - l, k);
27+
}
28+
29+
y = y.prev;
30+
}
31+
32+
const w = y.prev;
33+
_rotate_to(x, y, z);
34+
return [y, w];
35+
}

src/_rotate_right.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
import _rotate_to from './_rotate_to.js';
4+
5+
/**
6+
* Rotate list to the right k steps. The parameter k must be positive and
7+
* smaller than the length of the list.
8+
*
9+
* @param {Node} x The current first node.
10+
* @param {Node} z The current last node.
11+
* @param {number} k MUST be positive and smaller than the length of the list.
12+
* @return {[Node, Node]} The new first and last nodes.
13+
*/
14+
export default function _rotate_right(x, z, k) {
15+
assert(Number.isInteger(k));
16+
assert(k > 0);
17+
assert(x instanceof Node);
18+
assert(x.prev === null);
19+
assert(z instanceof Node);
20+
assert(z.next === null);
21+
assert(x !== z);
22+
let y = x;
23+
do {
24+
y = y.next;
25+
assert(y instanceof Node);
26+
} while (--k);
27+
28+
const w = y.prev;
29+
_rotate_to(x, y, z);
30+
return [y, w];
31+
}

src/_rotate_right_modulo.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import assert from 'assert';
2+
import Node from './Node.js';
3+
import _rotate_right from './_rotate_right.js';
4+
5+
/**
6+
* Rotate non-empty list to the right n steps. The parameter n must be positive.
7+
*
8+
* @param {Node} x The current first node.
9+
* @param {Node} z The current last node.
10+
* @param {number} n List length, MUST be positive.
11+
* @param {number} k MUST be positive.
12+
* @return {[Node, Node]} The new first and last nodes.
13+
*/
14+
export default function _rotate_right_modulo(x, z, n, k) {
15+
assert(Number.isInteger(n));
16+
assert(n > 0);
17+
assert(Number.isInteger(k));
18+
assert(k > 0);
19+
assert(x instanceof Node);
20+
assert(x.prev === null);
21+
assert(z instanceof Node);
22+
assert(z.next === null);
23+
assert(x !== z);
24+
const m = k % n;
25+
return m === 0 ? [x, z] : _rotate_right(x, z, m);
26+
}

0 commit comments

Comments
 (0)