prex-es5
Version:
Async coordination primitives and extensions on top of ES6 Promises
266 lines (263 loc) • 8.78 kB
JavaScript
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Licensed under the Apache License, Version 2.0.
See LICENSE file in the project root for details.
***************************************************************************** */
import { isMissing, isIterable, isInstance, isFunction } from "./utils";
export class LinkedListNode {
constructor(value) {
/*@internal*/ this._list = undefined;
/*@internal*/ this._previous = undefined;
/*@internal*/ this._next = undefined;
this.value = value;
}
get list() {
return this._list;
}
get previous() {
if (this._previous && this._list && this !== this._list.first) {
return this._previous;
}
return undefined;
}
get next() {
if (this._next && this._list && this._next !== this._list.first) {
return this._next;
}
return undefined;
}
}
export class LinkedList {
constructor(iterable) {
this._head = undefined;
this._size = 0;
if (!isIterable(iterable, /*optional*/ true))
throw new TypeError("Object not iterable: iterable.");
if (!isMissing(iterable)) {
for (const value of iterable) {
this.push(value);
}
}
}
get first() {
return this._head;
}
get last() {
if (this._head) {
return this._head._previous;
}
return undefined;
}
get size() {
return this._size;
}
*values() {
for (const node of this.nodes()) {
yield node.value;
}
}
*nodes() {
let node;
let next = this.first;
while (next !== undefined) {
node = next;
next = node.next;
yield node;
}
}
*drain() {
for (const node of this.nodes()) {
this.deleteNode(node);
yield node.value;
}
}
find(value) {
for (let node = this.first; node; node = node.next) {
if (sameValue(node.value, value)) {
return node;
}
}
return undefined;
}
findLast(value) {
for (let node = this.last; node; node = node.previous) {
if (sameValue(node.value, value)) {
return node;
}
}
return undefined;
}
has(value) {
return this.find(value) !== undefined;
}
insertBefore(node, value) {
if (!isInstance(node, LinkedListNode, /*optional*/ true))
throw new TypeError("LinkedListNode expected: node");
if (!isMissing(node) && node.list !== this)
throw new Error("Wrong list.");
return this._insertNode(node, new LinkedListNode(value), 0 /* before */);
}
insertNodeBefore(node, newNode) {
if (!isInstance(node, LinkedListNode, /*optional*/ true))
throw new TypeError("LinkedListNode expected: node");
if (!isInstance(newNode, LinkedListNode))
throw new TypeError("LinkedListNode expected: newNode");
if (!isMissing(node) && node.list !== this)
throw new Error("Wrong list.");
if (!isMissing(newNode.list))
throw new Error("Node is already attached to a list.");
this._insertNode(node, newNode, 0 /* before */);
}
insertAfter(node, value) {
if (!isInstance(node, LinkedListNode, /*optional*/ true))
throw new TypeError("LinkedListNode expected: node");
if (!isMissing(node) && node.list !== this)
throw new Error("Wrong list.");
return this._insertNode(node, new LinkedListNode(value), 1 /* after */);
}
insertNodeAfter(node, newNode) {
if (!isInstance(node, LinkedListNode, /*optional*/ true))
throw new TypeError("LinkedListNode expected: node");
if (!isInstance(newNode, LinkedListNode))
throw new TypeError("LinkedListNode expected: newNode");
if (!isMissing(node) && node.list !== this)
throw new Error("Wrong list.");
if (!isMissing(newNode.list))
throw new Error("Node is already attached to a list.");
this._insertNode(node, newNode, 1 /* after */);
}
push(value) {
return this._insertNode(undefined, new LinkedListNode(value), 1 /* after */);
}
pushNode(newNode) {
if (!isInstance(newNode, LinkedListNode))
throw new TypeError("LinkedListNode expected: newNode");
if (!isMissing(newNode.list))
throw new Error("Node is already attached to a list.");
this._insertNode(undefined, newNode, 1 /* after */);
}
pop() {
let node = this.popNode();
return node ? node.value : undefined;
}
popNode() {
let node = this.last;
if (this.deleteNode(node)) {
return node;
}
}
shift() {
let node = this.shiftNode();
return node ? node.value : undefined;
}
shiftNode() {
let node = this.first;
if (this.deleteNode(node)) {
return node;
}
}
unshift(value) {
return this._insertNode(undefined, new LinkedListNode(value), 0 /* before */);
}
unshiftNode(newNode) {
if (!isInstance(newNode, LinkedListNode))
throw new TypeError("LinkedListNode expected: newNode");
if (!isMissing(newNode.list))
throw new Error("Node is already attached to a list.");
this._insertNode(undefined, newNode, 0 /* before */);
}
delete(value) {
return this.deleteNode(this.find(value));
}
deleteNode(node) {
if (isMissing(node) || node.list !== this) {
return false;
}
return this._deleteNode(node);
}
deleteAll(predicate, thisArg) {
if (!isFunction(predicate))
throw new TypeError("Function expected: predicate");
let count = 0;
let node = this.first;
while (node) {
let next = node.next;
if (predicate.call(thisArg, node.value, node, this) && node.list === this) {
this._deleteNode(node);
++count;
}
node = next;
}
return count;
}
clear() {
while (this.size > 0) {
this.deleteNode(this.last);
}
}
forEach(callback, thisArg) {
if (!isFunction(callback))
throw new TypeError("Function expected: predicate");
for (const node of this.nodes()) {
callback.call(thisArg, node.value, node, this);
}
}
_deleteNode(node) {
if (node._next === node) {
this._head = undefined;
}
else {
node._next._previous = node._previous;
node._previous._next = node._next;
if (this._head === node) {
this._head = node._next;
}
}
node._list = undefined;
node._next = undefined;
node._previous = undefined;
this._size--;
return true;
}
_insertNode(adjacentNode, newNode, position) {
newNode._list = this;
if (this._head === undefined) {
newNode._next = newNode;
newNode._previous = newNode;
this._head = newNode;
}
else {
switch (position) {
case 0 /* before */:
if (adjacentNode === undefined) {
adjacentNode = this._head;
this._head = newNode;
}
else if (adjacentNode === this._head) {
this._head = newNode;
}
newNode._next = adjacentNode;
newNode._previous = adjacentNode._previous;
adjacentNode._previous._next = newNode;
adjacentNode._previous = newNode;
break;
case 1 /* after */:
if (adjacentNode === undefined) {
adjacentNode = this._head._previous;
}
newNode._previous = adjacentNode;
newNode._next = adjacentNode._next;
adjacentNode._next._previous = newNode;
adjacentNode._next = newNode;
break;
}
}
this._size++;
return newNode;
}
}
LinkedList.prototype[Symbol.iterator] = LinkedList.prototype.values;
function sameValue(x, y) {
return (x === y) ? (x !== 0 || 1 / x === 1 / y) : (x !== x && y !== y);
}
//# sourceMappingURL=list.js.map