data-structures
Version:
JavaScript data structures written in CoffeeScript.
278 lines (241 loc) • 7.65 kB
JavaScript
/*
Doubly Linked.
## Overview example:
```js
var list = new LinkedList([5, 4, 9]);
list.add(12); // => 12
list.head.next.value; // => 4
list.tail.value; // => 12
list.at(-1); // => 12
list.removeAt(2); // => 9
list.remove(4); // => 4
list.indexOf(5); // => 0
list.add(5, 1); // => 5. Second 5 at position 1.
list.indexOf(5, 1); // => 1
```
## Properties:
- head: first item.
- tail: last item.
- size: total number of items.
- item.value: value passed to the item when calling `add()`.
- item.prev: previous item.
- item.next: next item.
*/
(function() {
var LinkedList;
LinkedList = (function() {
function LinkedList(valuesToAdd) {
var value, _i, _len;
if (valuesToAdd == null) {
valuesToAdd = [];
}
/*
Can pass an array of elements to link together during `new LinkedList()`
initiation.
*/
this.head = {
prev: void 0,
value: void 0,
next: void 0
};
this.tail = {
prev: void 0,
value: void 0,
next: void 0
};
this.size = 0;
for (_i = 0, _len = valuesToAdd.length; _i < _len; _i++) {
value = valuesToAdd[_i];
this.add(value);
}
}
LinkedList.prototype.at = function(position) {
/*
Get the item at `position` (optional). Accepts negative index:
```js
myList.at(-1); // Returns the last element.
```
However, passing a negative index that surpasses the boundary will return
undefined:
```js
myList = new LinkedList([2, 6, 8, 3])
myList.at(-5); // Undefined.
myList.at(-4); // 2.
```
_Returns:_ item gotten, or undefined if not found.
*/
var currentNode, i, _i, _j, _ref;
if (!((-this.size <= position && position < this.size))) {
return;
}
position = this._adjust(position);
if (position * 2 < this.size) {
currentNode = this.head;
for (i = _i = 1; _i <= position; i = _i += 1) {
currentNode = currentNode.next;
}
} else {
currentNode = this.tail;
for (i = _j = 1, _ref = this.size - position - 1; _j <= _ref; i = _j += 1) {
currentNode = currentNode.prev;
}
}
return currentNode;
};
LinkedList.prototype.add = function(value, position) {
var currentNode, nodeToAdd, _ref, _ref1, _ref2;
if (position == null) {
position = this.size;
}
/*
Add a new item at `position` (optional). Defaults to adding at the end.
`position`, just like in `at()`, can be negative (within the negative
boundary). Position specifies the place the value's going to be, and the old
node will be pushed higher. `add(-2)` on list of size 7 is the same as
`add(5)`.
_Returns:_ item added.
*/
if (!((-this.size <= position && position <= this.size))) {
return;
}
nodeToAdd = {
value: value
};
position = this._adjust(position);
if (this.size === 0) {
this.head = nodeToAdd;
} else {
if (position === 0) {
_ref = [nodeToAdd, this.head, nodeToAdd], this.head.prev = _ref[0], nodeToAdd.next = _ref[1], this.head = _ref[2];
} else {
currentNode = this.at(position - 1);
_ref1 = [currentNode.next, nodeToAdd, nodeToAdd, currentNode], nodeToAdd.next = _ref1[0], (_ref2 = currentNode.next) != null ? _ref2.prev = _ref1[1] : void 0, currentNode.next = _ref1[2], nodeToAdd.prev = _ref1[3];
}
}
if (position === this.size) {
this.tail = nodeToAdd;
}
this.size++;
return value;
};
LinkedList.prototype.removeAt = function(position) {
var currentNode, valueToReturn, _ref;
if (position == null) {
position = this.size - 1;
}
/*
Remove an item at index `position` (optional). Defaults to the last item.
Index can be negative (within the boundary).
_Returns:_ item removed.
*/
if (!((-this.size <= position && position < this.size))) {
return;
}
if (this.size === 0) {
return;
}
position = this._adjust(position);
if (this.size === 1) {
valueToReturn = this.head.value;
this.head.value = this.tail.value = void 0;
} else {
if (position === 0) {
valueToReturn = this.head.value;
this.head = this.head.next;
this.head.prev = void 0;
} else {
currentNode = this.at(position);
valueToReturn = currentNode.value;
currentNode.prev.next = currentNode.next;
if ((_ref = currentNode.next) != null) {
_ref.prev = currentNode.prev;
}
if (position === this.size - 1) {
this.tail = currentNode.prev;
}
}
}
this.size--;
return valueToReturn;
};
LinkedList.prototype.remove = function(value) {
/*
Remove the item using its value instead of position. **Will remove the fist
occurrence of `value`.**
_Returns:_ the value, or undefined if value's not found.
*/
var currentNode;
if (value == null) {
return;
}
currentNode = this.head;
while (currentNode && currentNode.value !== value) {
currentNode = currentNode.next;
}
if (!currentNode) {
return;
}
if (this.size === 1) {
this.head.value = this.tail.value = void 0;
} else if (currentNode === this.head) {
this.head = this.head.next;
this.head.prev = void 0;
} else if (currentNode === this.tail) {
this.tail = this.tail.prev;
this.tail.next = void 0;
} else {
currentNode.prev.next = currentNode.next;
currentNode.next.prev = currentNode.prev;
}
this.size--;
return value;
};
LinkedList.prototype.indexOf = function(value, startingPosition) {
var currentNode, position;
if (startingPosition == null) {
startingPosition = 0;
}
/*
Find the index of an item, similarly to `array.indexOf()`. Defaults to start
searching from the beginning, by can start at another position by passing
`startingPosition`. This parameter can also be negative; but unlike the
other methods of this class, `startingPosition` (optional) can be as small
as desired; a value of -999 for a list of size 5 will start searching
normally, at the beginning.
**Note:** searches forwardly, **not** backwardly, i.e:
```js
var myList = new LinkedList([2, 3, 1, 4, 3, 5])
myList.indexOf(3, -3); // Returns 4, not 1
```
_Returns:_ index of item found, or -1 if not found.
*/
if (((this.head.value == null) && !this.head.next) || startingPosition >= this.size) {
return -1;
}
startingPosition = Math.max(0, this._adjust(startingPosition));
currentNode = this.at(startingPosition);
position = startingPosition;
while (currentNode) {
if (currentNode.value === value) {
break;
}
currentNode = currentNode.next;
position++;
}
if (position === this.size) {
return -1;
} else {
return position;
}
};
LinkedList.prototype._adjust = function(position) {
if (position < 0) {
return this.size + position;
} else {
return position;
}
};
return LinkedList;
})();
module.exports = LinkedList;
}).call(this);