Doublie
Doubly circular & linear linked lists for ES6
Install / Use
/learn @klaudiosinani/DoublieREADME
Description
ES6 implementation of the circular and linear doubly linked list data structures with TypeScript support.
Visit the contributing guidelines to learn more on how to translate this document into more languages.
Contents
Install
Yarn
yarn add doublie
NPM
npm install doublie
Usage
Doublie exposes a chainable API, that can be utilized through a simple and minimal syntax, allowing you to combine methods effectively.
Usage examples can be also found at the test directory.
'use strict';
const {Circular, Linear} = require('doublie');
const linear = new Linear();
linear.prepend('A').append('B');
linear.head;
// => Node { next: Node { next: null, prev: [Circular], value: 'B' }, prev: null, value: 'A' }
linear.head.next;
// => Node { next: null, prev: Node { next: [Circular], prev: null, value: 'A' }, value: 'B' }
linear.head.next.next;
// => null
linear.head.prev;
// => null
linear.map(x => `[${x}]`).reverse().join(' -> ');
// => [B] -> [A]
const circular = new Circular();
circular.append(2).prepend(1);
circular.head;
// => Node {
// next: Node { next: [Circular], prev: [Circular], value: 2 },
// prev: Node { next: [Circular], prev: [Circular], value: 1 },
// value: 'A' }
circular.head.next;
// => Node {
// next: Node { next: [Circular], prev: [Circular], value: 1 },
// prev: Node { next: [Circular], prev: [Circular], value: 1 },
// value: 2 }
circular.head.next.next;
// => Node {
// next: Node { next: [Circular], prev: [Circular], value: 2 },
// prev: Node { next: [Circular], prev: [Circular], value: 2 },
// value: 1 }
circular.head.prev;
// => Node {
// next: Node { next: [Circular], prev: [Circular], value: 1 },
// prev: Node { next: [Circular], prev: [Circular], value: 1 },
// value: 2 }
circular.reduce((x, y) => x > y ? x : y, -Infinity);
// => 2
In Depth
Linear Doubly Linked List
Linear doubly linked lists can contain multiple nodes, where each node has only a value, a prev and next attribute. The value attribute holds the value stored inside of the node and the prev & next attributes point to the previous and next nodes in line respectively. The only exceptions are that the first node of the list has null stored to its prev attribute, indicating the lack of nodes before it, thus the beginning of the list, and the last node has null stored to its next attribute, indicating the lack of further nodes down the line, thus the end of the list.
The following example demonstrates the operations that can be performed on any linear doubly linked list.
'use strict';
const {Linear} = require('doublie');
const linear = new Linear();
// Append a node holding the value `E`
linear.append('E');
linear.head; // => Node { value: 'E', next: null, prev: null }
linear.last; // => Node { value: 'E', next: null, prev: null }
linear.get(0); // => E
// Return the node corresponding to the index
linear.node(0); // => Node { value: 'E', next: null, prev: null }
linear.node(0).value; // => E
linear.node(0).next; // => null
linear.node(0).prev; // => null
// Append multiple nodes at once
linear.append('F', 'G');
linear.length; // => 3
linear.node(0).next.value; // => F
linear.node(0).next.next.value; // => G
linear.toArray(); // => [ 'E', 'F', 'G' ]
// Prepend multiple nodes at once
linear.prepend('B', 'A');
linear.join(' '); // => A B E F G
// Insert multiple nodes to the given index
linear.insert({value: ['D', 'C', 'X'], index: 2});
linear.join(' '); // => A B X C D E F G
// Remove the node corresponding to the index
linear.remove(2);
linear.join(' '); // => A B C D E F G
// Update the value of the node corresponding to the index
linear.node(linear.length - 1).value = '!';
linear.join(' '); // => A B C D E F !
linear.set({value: 'G', index: linear.length - 1});
linear.join(' '); // => A B C D E F G
// Iterate over the list
const array = [];
linear.forEach(x => array.push(x));
// => [ 'A', 'B', 'C', 'D', 'E', 'F', 'G' ]
// Chain multiple methods
linear.reverse().map(x => `[${x}]`).join('->');
// => [G]->[F]->[E]->[D]->[C]->[B]->[A]
// Clear the list
linear.clear(); // => Linear { head: null, length: 0, last: null }
// Reduce values to a final sum
linear.append(5, 10, 15, 20, 25).reduce((x, y) => x + y, 0);
//=> 75
Circular Doubly Linked List
Circular doubly linked lists can also contain multiple nodes, where again each node has the same value, prev and next attributes. The only difference compared to linear lists, is that the last node always points back to the first node/head of the list, and similarly the first node/head points back to the last node of the list, thus the list is said to be circular or circularly linked.
The following example demonstrates the operations that can be performed on any circular doubly linked list.
'use strict';
const {Circular} = require('doublie');
const circular = new Circular();
// Append a node holding the value `E`
circular.append('E');
circular.head; // => Node { value: 'E', next: [Circular], prev: [Circular] }
circular.last; // => Node { value: 'E', next: [Circular], prev: [Circular] }
circular.get(0); // => E
// Return the node corresponding to the index
circular.node(0); // => Node { value: 'E', next: [Circular], prev: [Circular] }
circular.node(0).value; // => E
circular.node(0).next.value; // => E
circular.node(0).next.next.value; // => E
circular.node(0).prev.value; // => E
circular.node(0).prev.prev.value; // => E
// Append multiple nodes at once
circular.append('F', 'G');
circular.length; // => 3
circular.node(0).next.value; // => F
circular.node(0).next.next.value; // => G
circular.node(0).next.next.next.value; // => E
circular.node(0).prev.value; // => G
circular.node(0).prev.prev.value; // => F
circular.node(0).prev.prev.prev.value; // => E
circular.toArray(); // => [ 'E', 'F', 'G' ]
// Prepend multiple nodes at once
circular.prepend('B', 'A');
circular.join(' '); // => A B E F G
// Insert multiple nodes to the given index
circular.insert({value: ['D', 'C', 'X'], index: 2});
circular.join(' '); // => A B X C D E F G
// Remove the node corresponding to the index
circular.remove(2);
circular.join(' '); // => A B C D E F G
// Update the value of the node corresponding to the index
circular.node(circular.length - 1).value = '!';
circular.join(' '); // => A B C D E F !
circular.set({value: 'G', index: circular.length - 1});
circular.join(' '); // => A B C D E F G
// Iterate over the list
const array = [];
circular.forEach(x => array.push(x));
// => [ 'A', 'B', 'C', 'D', 'E', 'F', 'G' ]
// Chain multiple methods
circular.reverse().map(x => `[${x}]`).join('->');
// => [G]->[F]->[E]->[D]->[C]->[B]->[A]
// Clear the list
circular.clear(); // => Circular { head: null, length: 0, last: null }
// Reduce values to a final sum
circular.append(5, 10, 15, 20, 25).reduce((x, y) => x + y, 0);
//=> 75
API
The following documentation holds for both circular & linear lists. The described list instance is used to depict the same methods that are applicable to both a linear and a circular linked list, without overlooking their above described differences and unique qualities.
list.append(value[, value])
- Return Type:
Linked List
Appends one of more nodes to the list.
value
- Type:
any
Can be one or more comma delimited values. Each value corresponds to a single node.
list.append('A', 'B', 'C', 'D');
// => { value: 'A', prev: [List], next: { value: 'B', next: [List] } }
list.prepend(value[, value])
- Return Type:
Linked List
Prepends one of more nodes to the list.
value
- Type:
any
Can be one or more comma delimited values. Each value corresponds to a single node.
list.append('C' , 'D');
// => { value: 'C', next: [List], prev: [List] }
list.prepend('B', 'A');
// => { value: 'A', prev: [List], next: { value: 'B', prev: [List], next: { value: 'C', prev[List], next: [List] } } }
list.head
- Return Type:
Node | null
Returns the first node / head on the list.
list.append('A', 'B');
list.head;
// => Node { value: 'A', prev: [List], next: [List] }
list.last
- Return Type:
Node | null
Returns the last node on the list.
list.append('A', 'B');
list.last;
// => Node { value: 'B', prev: [List], next: [List] }
list.length
- Return Type:
Integer
Returns the length of the list.
list.append('A', 'B');
list.length;
// => 2
list.isEmpty()
- Return Type:
Boolean
Checks whether or not the list is empty.
list.append('A', 'B');
list.isEmpty();
// => false
list.insert({value[, value], index})
- Return Type:
Linked List
Inserts one or more nodes to the given index.
value
- Type:
any
Can be one or more comma delimited values. Each value corresponds to a single node.
index
- Type:
Integer
Can be an integer corresponding to a list index.
list.append('A', 'B', 'E');
list.insert({value: ['C', 'D'], index: 1});
// => { value: 'A', prev: [List], next: { value: 'D', prev: [List], next: { value: 'C', prev: [Lis
Related Skills
node-connect
349.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Writing Hookify Rules
109.5kThis skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
review-duplication
100.3kUse this skill during code reviews to proactively investigate the codebase for duplicated functionality, reinvented wheels, or failure to reuse existing project best practices and shared utilities.
