Skip to content

Instantly share code, notes, and snippets.

@uhop
Last active October 11, 2019 21:46
Show Gist options
  • Save uhop/f17409a95a14f10e1cbd5a584ca9d245 to your computer and use it in GitHub Desktop.
Save uhop/f17409a95a14f10e1cbd5a584ca9d245 to your computer and use it in GitHub Desktop.
Simple yet complete implementation of a double-linked list.
'use strict';
class ListNode {
constructor() {
this.prev = this.next = this;
}
}
const pop = head => {
const rest = head.next;
head.prev.next = head.next;
head.next.prev = head.prev;
head.prev = head.next = head;
return {node: head, head: rest};
};
const extract = (from, to) => {
const prev = from.prev,
next = to.next;
prev.next = next;
next.prev = prev;
from.prev = to;
to.next = from;
return from;
};
const splice = (head1, head2) => {
const tail1 = head1.prev,
tail2 = head2.prev;
tail1.next = head2;
head2.prev = tail1;
tail2.next = head1;
head1.prev = tail2;
return head1;
};
class ListValueNode extends ListNode {
constructor(value) {
super();
this.value = value;
}
pop() {
return pop(this).node.value;
}
addBefore(value) {
splice(this, new ListValueNode(value));
return this;
}
addAfter(value) {
splice(this.next, new ListValueNode(value));
return this;
}
insertBefore(list) {
splice(this, pop(list).head);
}
insertAfter(list) {
splice(this.next, pop(list).head);
}
}
class List extends ListNode {
get isEmpty() {
return this.prev === this;
}
get front() {
return this.next;
}
get back() {
return this.prev;
}
getLength() {
let n = 0;
for (let p = this.next; p !== this; ++n, p = p.next);
return n;
}
clear() {
this.prev = this.next = this;
return this;
}
remove(from, to = from) {
extract(from, to);
return this;
}
extract(from, to) {
return splice(new List(), extract(from, to));
}
reverse() {
let next = this.next;
this.next = this.prev;
this.prev = next;
while (next !== this) {
const node = next;
next = node.next;
node.next = node.prev;
node.prev = next;
}
return this;
}
popFront() {
if (this.prev !== this) {
return this.next.pop();
}
}
popBack() {
if (this.prev !== this) {
return this.prev.pop();
}
}
pushFront(value) {
splice(this.next, new ListValueNode(value));
return this;
}
pushBack(value) {
splice(this, new ListValueNode(value));
return this;
}
appendFront(list) {
if (list.prev !== list) {
splice(this.next, extract(list.next, list.prev));
}
return this;
}
appendBack(list) {
if (list.prev !== list) {
splice(this, extract(list.next, list.prev));
}
return this;
}
moveToFront(node) {
if (this.next !== node) {
splice(this.next, extract(node, node));
}
return this;
}
moveToBack(node) {
if (this.prev !== node) {
splice(this, extract(node, node));
}
return this;
}
[Symbol.iterator]() {
let current = this.next;
return {
next: () => {
if (current === this) return {done: true};
current = current.next;
return {value: current.prev.value};
}
};
}
getReverseIterable() {
return {
[Symbol.iterator]: () => {
let current = this.prev;
return {
next: () => {
if (current === this) return {done: true};
current = current.prev;
return {value: current.next.value};
}
};
}
};
}
static from(values) {
const list = new List();
for (const value of values) {
list.pushBack(value);
}
return list;
}
}
module.exports = List;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment