// Doubly linked list prototype.
// Node.
function Node(data) {
this.next = this;
this.prev = this;
this.data = data;
}
function nodeRemove(at) {
let next = at.next;
let prev = at.prev;
next.prev = prev;
prev.next = next;
}
function nodeInsertBefore(at, node) {
node.next = at;
node.prev = at.prev;
at.prev.next = node;
at.prev = node;
}
function nodeTransferBefore(at, first, last) {
if (first === last || last === at)
return;
at.prev.next = first;
last.prev.next = at;
first.prev.next = last;
let temp = at.prev;
at.prev = last.prev;
last.prev = first.prev;
first.prev = temp;
}
// List.
function List(arrayLike) {
this.head = new Node();
if (arrayLike === undefined)
return;
for (let x of arrayLike) {
this.pushBack(x);
}
}
List.prototype.begin = function () {
return this.head.next;
};
List.prototype.end = function () {
return this.head;
};
List.prototype.empty = function () {
return this.begin() === this.end();
};
List.prototype.front = function () {
if (this.empty())
throw new Error("Data access in empty list");
return this.begin().data;
};
List.prototype.back = function () {
if (this.empty())
throw new Error("Data access in empty list");
return this.end().prev.data;
};
List.prototype.pushFront = function (data) {
nodeInsertBefore(this.begin(), new Node(data));
return this;
};
List.prototype.pushBack = function (data) {
nodeInsertBefore(this.end(), new Node(data));
return this;
};
List.prototype.popFront = function () {
if (this.empty())
throw new Error("Pop in empty list");
nodeRemove(this.begin());
return this;
};
List.prototype.popBack = function (data) {
if (this.empty())
throw new Error("Pop in empty list");
nodeRemove(this.end().prev);
return this;
};
List.prototype.splice = function (at, first, last) {
nodeTransferBefore(at, first, last);
return this;
};
List.prototype[Symbol.iterator] = function* () {
let first = this.begin(), last = this.end();
for (; first !== last; first = first.next) {
yield first.data;
}
};
List.prototype.forEach = function (f, ...args) {
for (let data of this) {
f(data, ...args);
}
};
List.prototype.map = function (f, ...args) {
let result = new List();
this.forEach(x => result.pushBack(f(x, ...args)));
return result;
};
List.prototype.size = function () {
let count = 0;
this.forEach(_ => ++count);
return count;
};
List.prototype.toString = function () {
return `List {${Array.from(this).join(", ")}}`;
};
// Main.
let list1 = new List();
console.log(list1.toString());
console.log("list1.size:", list1.size());
console.log("list1.empty:", list1.empty());
for (let i = 0; i < 5; i++) {
list1.pushFront(i);
}
console.log(list1.toString());
console.log("list1.size:", list1.size());
console.log("list1.empty:", list1.empty());
let list2 = new List([10, 11, 12, 13]);
console.log(list2.toString());
console.log("list2.size:", list2.size());
list1.splice(list1.begin(), list2.begin(), list2.end());
console.log(list1.toString());
console.log("list1.size:", list1.size());
console.log("list1.front:", list1.front());
console.log("list1.back:", list1.back());
console.log(list2.toString());
try {
list2.front();
} catch (error) {
console.log(error.message);
}
try {
list2.back();
} catch (error) {
console.log(error.message);
}
try {
list2.popFront();
} catch (error) {
console.log(error.message);
}
try {
list2.popBack();
} catch (error) {
console.log(error.message);
}