// Doubly linked list prototype. (2.02)
function Node(data) {
this.next = this;
this.prev = this;
this.data = data;
}
Node.prototype.remove = function () {
let next = this.next;
let prev = this.prev;
next.prev = prev;
prev.next = next;
};
Node.prototype.insert = function (node) {
node.next = this;
node.prev = this.prev;
this.prev.next = node;
this.prev = node;
};
Node.prototype.splice = function (first, last) {
if (first === last || last === this)
return;
last.prev.next = this;
first.prev.next = last;
this.prev.next = first;
let temp = this.prev;
this.prev = last.prev;
last.prev = first.prev;
first.prev = temp;
};
Node.prototype.step = function (n = 1) {
let result = this;
for (let i = 0; i < n; i++) {
result = result.next;
}
for (let i = 0; i > n; i--) {
result = result.prev;
}
return result;
};
Node.prototype.distance = function (last) {
let count = 0;
for (let first = this; first !== last; first = first.next) {
count++;
}
return count;
};
// List.
function List(arrayLike = []) {
this.clear();
for (let x of arrayLike) {
this.pushBack(x);
}
}
List.prototype.clear = function () {
this.head = new Node();
return this;
};
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 (value) {
this.begin().insert(new Node(value));
return this;
};
List.prototype.pushBack = function (value) {
this.end().insert(new Node(value));
return this;
};
List.prototype.popFront = function () {
if (this.empty())
throw new Error("Pop in empty list");
this.begin().remove();
return this;
};
List.prototype.popBack = function () {
if (this.empty())
throw new Error("Pop in empty list");
this.end().prev.remove();
return this;
};
List.prototype[Symbol.iterator] = function* () {
const last = this.end();
for (let first = this.begin(); first !== last; first = first.next) {
yield first.data;
}
};
List.prototype.forEach = function (callbackFn, ...args) {
for (let x of this) {
callbackFn(x, ...args);
}
};
List.prototype.map = function (callbackFn, ...args) {
let result = new List();
this.forEach(x => result.pushBack(callbackFn(x, ...args)));
return result;
};
List.prototype.toString = function () {
return `List [${Array.from(this).join(", ")}]`;
};
// Main.
let list1 = new List();
console.log("1." + list1);
console.log("1.begin.distance(1.end):", list1.begin().distance(list1.end()));
console.log("1.empty:", list1.empty());
for (let i = 0; i < 5; i++) {
list1.pushFront(i);
}
console.log("1." + list1);
console.log("1.begin.distance(1.end):", list1.begin().distance(list1.end()));
console.log("1.empty:", list1.empty());
console.log();
let list2 = new List([10, 11, 12, 13]);
console.log("2." + list2);
console.log("2.begin.distance(2.end):", list2.begin().distance(list2.end()));
console.log();
console.log("1.begin.step.splice(2.begin, 2.end)");
list1.begin().step().splice(list2.begin(), list2.end());
console.log("1." + list1);
console.log("1.begin.distance(1.end):", list1.begin().distance(list1.end()));
console.log("1.front:", list1.front());
console.log("1.back:", list1.back());
console.log();
console.log("2." + list2);
console.log("2.begin.distance(2.end):", list2.begin().distance(list2.end()));
try {
list2.front();
} catch (error) {
console.log("2.front:", error.message);
}
try {
list2.back();
} catch (error) {
console.log("2.back:", error.message);
}
try {
list2.popFront();
} catch (error) {
console.log("2.popFront:", error.message);
}
try {
list2.popBack();
} catch (error) {
console.log("2.popBack:", error.message);
}