// Doubly linked list prototype. (3.00)
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 result = 0;
for (let first = this; first !== last; first = first.next) {
result++;
}
return result;
};
// List.
function List(arrayLike = []) {
this.clear();
for (let x of arrayLike) {
this.pushBack(x);
}
}
Object.defineProperty(List.prototype, 'begin', {
get: function () {
return this.head.next;
}
});
Object.defineProperty(List.prototype, 'end', {
get: function () {
return this.head;
}
});
Object.defineProperty(List.prototype, 'empty', {
get: function () {
return this.begin === this.end;
}
});
Object.defineProperty(List.prototype, 'front', {
get: function () {
if (this.empty)
throw new Error("Data access in empty list");
return this.begin.data;
}
});
Object.defineProperty(List.prototype, 'back', {
get: function () {
if (this.empty)
throw new Error("Data access in empty list");
return this.end.prev.data;
}
});
List.prototype.clear = function () {
this.head = new Node();
return this;
};
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);
}