linkd
Advanced tools
Comparing version 1.0.1 to 2.0.0
1226
lib/Linkd.js
!function() { | ||
'use strict'; | ||
'use strict'; | ||
@@ -7,7 +7,7 @@ | ||
var Class = require('ee-class') | ||
, log = require('ee-log') | ||
, EventEmitter = require('ee-event-emitter') | ||
, Node = require('./Node') | ||
; | ||
const log = require('ee-log'); | ||
const EventEmitter = require('ee-event-emitter'); | ||
const Node = require('./Node'); | ||
@@ -18,19 +18,15 @@ | ||
/** | ||
* A fast and extendable doubly linked list. | ||
*/ | ||
/** | ||
* A fast and extendable doubly linked list. | ||
*/ | ||
var classDefinition = { | ||
inherits: EventEmitter | ||
module.exports = class Linkd extends EventEmitter { | ||
// the lenght property returns how many items are currently | ||
// stored in the linked list | ||
, length: { | ||
get: function() {return this.map.size;} | ||
, set: function() {throw new Error('The length property is readonly!')} | ||
} | ||
// the lenght property returns how many items are currently | ||
// stored in the linked list | ||
get length() {return this.map.size;} | ||
@@ -41,66 +37,67 @@ | ||
/** | ||
* initilize the linked list, set up storage and pointer. class constructor. | ||
* | ||
* @param {CustomNodeType} CustomNodeType A custom item implementation | ||
* used for storing items | ||
*/ | ||
, init: function(CustomType) { | ||
/** | ||
* initilize the linked list, set up storage and pointer. class constructor. | ||
* | ||
* @param {CustomNodeType} CustomNodeType A custom item implementation | ||
* used for storing items | ||
*/ | ||
constructor(CustomType) { | ||
super(); | ||
// the map hoofding the linked list | ||
Class.define(this, 'map', Class(new Map()).Writable()); | ||
// the map hoofding the linked list | ||
Class.define(this, 'map', Class(new Map()).Writable()); | ||
// reference to the first item | ||
Class.define(this, 'firsNode', Class(null).Writable().Configurable()); | ||
// reference to the first item | ||
Class.define(this, 'firsNode', Class(null).Writable().Configurable()); | ||
// reference to the last item | ||
Class.define(this, 'lastNode', Class(null).Writable().Configurable()); | ||
// reference to the last item | ||
Class.define(this, 'lastNode', Class(null).Writable().Configurable()); | ||
// the user may pass a custom type for the map items | ||
if (CustomType){ | ||
if (new CustomType().identifier === Node.identifier) throw new Error('The custom type must inherit from the LinkedList.Node class!'); | ||
Class.define(this, 'Node', Class(CustomType)); | ||
} | ||
else { | ||
Class.define(this, 'Node', Class(Node)); | ||
} | ||
// the user may pass a custom type for the map items | ||
if (CustomType){ | ||
if (new CustomType().identifier === Node.identifier) throw new Error('The custom type must inherit from the LinkedList.Node class!'); | ||
Class.define(this, 'Node', Class(CustomType)); | ||
} | ||
else { | ||
Class.define(this, 'Node', Class(Node)); | ||
} | ||
// emit the drain event if the list is empty | ||
this.on('remove', () => { | ||
if (!this.length) this.emit('drain'); | ||
}) | ||
} | ||
// emit the drain event if the list is empty | ||
this.on('remove', () => { | ||
if (!this.length) this.emit('drain'); | ||
}) | ||
} | ||
/** | ||
* returns a node by its hash or null if the | ||
* node was not found | ||
* | ||
* @private | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @returns {node|null} node or null | ||
*/ | ||
, _getNode: function(hash) { | ||
return this.has(hash) ? this.map.get(hash) : null; | ||
} | ||
/** | ||
* returns a node by its hash or null if the | ||
* node was not found | ||
* | ||
* @private | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @returns {node|null} node or null | ||
*/ | ||
_getNode(hash) { | ||
return this.has(hash) ? this.map.get(hash) : null; | ||
} | ||
/** | ||
* returns an item by its hash or undefined if the | ||
* item was not found. | ||
* | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @returns {*|undefined} value value of any type | ||
*/ | ||
, get: function(hash) { | ||
var node = this.getNode(hash); | ||
/** | ||
* returns an item by its hash or undefined if the | ||
* item was not found. | ||
* | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @returns {*|undefined} value value of any type | ||
*/ | ||
get(hash) { | ||
let node = this.getNode(hash); | ||
return node ? node.value : undefined; | ||
} | ||
return node ? node.value : undefined; | ||
} | ||
@@ -110,21 +107,21 @@ | ||
/** | ||
* returns a node by its hash or null if the | ||
* node was not found | ||
* | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @returns {node|null} node or null | ||
*/ | ||
, getNode: function(hash) { | ||
var node = this._getNode(hash); | ||
/** | ||
* returns a node by its hash or null if the | ||
* node was not found | ||
* | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @returns {node|null} node or null | ||
*/ | ||
getNode(hash) { | ||
let node = this._getNode(hash); | ||
if (node) { | ||
// emit events | ||
this.emit('get', node.value); | ||
this.emit('getNode', node); | ||
if (node) { | ||
// emit events | ||
this.emit('get', node.value); | ||
this.emit('getNode', node); | ||
return node; | ||
} | ||
else return null; | ||
} | ||
return node; | ||
} | ||
else return null; | ||
} | ||
@@ -134,45 +131,45 @@ | ||
/** | ||
* returns the first item or undefined | ||
* | ||
* | ||
* | ||
* @returns {*|undefined} item or undefined | ||
*/ | ||
, getFirst: function(noEvents) { | ||
return this.firstNode ? this.getFirstNode(noEvents).value : undefined; | ||
} | ||
/** | ||
* returns the first item or undefined | ||
* | ||
* | ||
* | ||
* @returns {*|undefined} item or undefined | ||
*/ | ||
getFirst(noEvents) { | ||
return this.firstNode ? this.getFirstNode(noEvents).value : undefined; | ||
} | ||
/** | ||
* returns the last item or undefined | ||
* | ||
* @returns {*|undefined} item or undefined | ||
*/ | ||
, getLast: function(noEvents) { | ||
return this.lastNode ? this.getLastNode(noEvents).value : undefined; | ||
} | ||
/** | ||
* returns the last item or undefined | ||
* | ||
* @returns {*|undefined} item or undefined | ||
*/ | ||
getLast(noEvents) { | ||
return this.lastNode ? this.getLastNode(noEvents).value : undefined; | ||
} | ||
/** | ||
* returns the first node or null | ||
* | ||
* @returns {Node|null} node or null | ||
*/ | ||
, getFirstNode: function(noEvents) { | ||
return this.firstNode ? (noEvents ? this._getNode(this.firstNode.hash) : this.getNode(this.firstNode.hash)) : null; | ||
} | ||
/** | ||
* returns the first node or null | ||
* | ||
* @returns {Node|null} node or null | ||
*/ | ||
getFirstNode(noEvents) { | ||
return this.firstNode ? (noEvents ? this._getNode(this.firstNode.hash) : this.getNode(this.firstNode.hash)) : null; | ||
} | ||
/** | ||
* returns the last node or null | ||
* | ||
* @returns {Node|null} item or null | ||
*/ | ||
, getLastNode: function(noEvents) { | ||
return this.lastNode ? (noEvents ? this._getNode(this.lastNode.hash) : this.getNode(this.lastNode.hash)) : null; | ||
} | ||
/** | ||
* returns the last node or null | ||
* | ||
* @returns {Node|null} item or null | ||
*/ | ||
getLastNode(noEvents) { | ||
return this.lastNode ? (noEvents ? this._getNode(this.lastNode.hash) : this.getNode(this.lastNode.hash)) : null; | ||
} | ||
@@ -183,11 +180,11 @@ | ||
/** | ||
* checks if a certain hash is present in the linked list | ||
* | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @returns {bool} true if the hash was found inside the list | ||
*/ | ||
, has: function(hash) { | ||
return this.map.has(hash); | ||
} | ||
/** | ||
* checks if a certain hash is present in the linked list | ||
* | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @returns {bool} true if the hash was found inside the list | ||
*/ | ||
has(hash) { | ||
return this.map.has(hash); | ||
} | ||
@@ -197,125 +194,125 @@ | ||
/** | ||
* Moves an item so that its placed after another item | ||
* | ||
* @param {*|Node} hash the hash of the item to move or the node | ||
* @param {*|Node} afterHash the hash to move the item after or the node | ||
* | ||
* @returns {Node} the node that was moved | ||
*/ | ||
, moveAfter: function(hash, afterHash) { | ||
var node; | ||
/** | ||
* Moves an item so that its placed after another item | ||
* | ||
* @param {*|Node} hash the hash of the item to move or the node | ||
* @param {*|Node} afterHash the hash to move the item after or the node | ||
* | ||
* @returns {Node} the node that was moved | ||
*/ | ||
moveAfter(hash, afterHash) { | ||
let node; | ||
// check the input | ||
if (hash instanceof this.Node) hash = hash.hash; | ||
if (afterHash instanceof this.Node) afterHash = afterHash.hash; | ||
// check the input | ||
if (hash instanceof this.Node) hash = hash.hash; | ||
if (afterHash instanceof this.Node) afterHash = afterHash.hash; | ||
// the items need to exist | ||
if (this.has(hash)) { | ||
if (this.has(afterHash)) { | ||
// remove the node | ||
node = this._removeNode(hash); | ||
// the items need to exist | ||
if (this.has(hash)) { | ||
if (this.has(afterHash)) { | ||
// remove the node | ||
node = this._removeNode(hash); | ||
// addd again | ||
this._addAfter(node, afterHash); | ||
// addd again | ||
this._addAfter(node, afterHash); | ||
// return the node | ||
return node; | ||
} | ||
else throw new Error('The node cannot be moved after the node with the hash «'+hash+'». It could not be found!'); | ||
} | ||
else throw new Error('The node cannot be moved, the hash «'+hash+'» could not be found!'); | ||
} | ||
// return the node | ||
return node; | ||
} | ||
else throw new Error('The node cannot be moved after the node with the hash «'+hash+'». It could not be found!'); | ||
} | ||
else throw new Error('The node cannot be moved, the hash «'+hash+'» could not be found!'); | ||
} | ||
/** | ||
* Moves an item so that its placed before another item | ||
* | ||
* @param {*|Node} hash the hash of the item to move or the node | ||
* @param {*|Node} beforeHash the hash to move the item before or the node | ||
* | ||
* @returns {Node} the node that was moved | ||
*/ | ||
, moveBefore: function(hash, beforeHash) { | ||
var node; | ||
/** | ||
* Moves an item so that its placed before another item | ||
* | ||
* @param {*|Node} hash the hash of the item to move or the node | ||
* @param {*|Node} beforeHash the hash to move the item before or the node | ||
* | ||
* @returns {Node} the node that was moved | ||
*/ | ||
moveBefore(hash, beforeHash) { | ||
let node; | ||
// check the input | ||
if (hash instanceof this.Node) hash = hash.hash; | ||
if (beforeHash instanceof this.Node) beforeHash = beforeHash.hash; | ||
// check the input | ||
if (hash instanceof this.Node) hash = hash.hash; | ||
if (beforeHash instanceof this.Node) beforeHash = beforeHash.hash; | ||
// the items need to exist | ||
if (this.has(hash)) { | ||
if (this.has(beforeHash)) { | ||
// remove the node | ||
node = this._removeNode(hash); | ||
// the items need to exist | ||
if (this.has(hash)) { | ||
if (this.has(beforeHash)) { | ||
// remove the node | ||
node = this._removeNode(hash); | ||
// addd again | ||
this._addBefore(node, beforeHash); | ||
// addd again | ||
this._addBefore(node, beforeHash); | ||
// return the node | ||
return node; | ||
} | ||
else throw new Error('The node cannot be moved after the node with the hash «'+hash+'». It could not be found!'); | ||
} | ||
else throw new Error('The node cannot be moved, the hash «'+hash+'» could not be found!'); | ||
} | ||
// return the node | ||
return node; | ||
} | ||
else throw new Error('The node cannot be moved after the node with the hash «'+hash+'». It could not be found!'); | ||
} | ||
else throw new Error('The node cannot be moved, the hash «'+hash+'» could not be found!'); | ||
} | ||
/** | ||
* Moves an item so that its placed at the beginning of the list | ||
* | ||
* @param {*|Node} hash the hash of the item to move or the node | ||
* | ||
* @returns {Node} the node that was moved | ||
*/ | ||
, moveToBegin: function(hash) { | ||
var node; | ||
/** | ||
* Moves an item so that its placed at the beginning of the list | ||
* | ||
* @param {*|Node} hash the hash of the item to move or the node | ||
* | ||
* @returns {Node} the node that was moved | ||
*/ | ||
moveToBegin(hash) { | ||
let node; | ||
// check the input | ||
if (hash instanceof this.Node) hash = hash.hash; | ||
// check the input | ||
if (hash instanceof this.Node) hash = hash.hash; | ||
// the items need to exist | ||
if (this.has(hash)) { | ||
// remove the node | ||
node = this._removeNode(hash); | ||
// the items need to exist | ||
if (this.has(hash)) { | ||
// remove the node | ||
node = this._removeNode(hash); | ||
// addd again | ||
this._push(node); | ||
// addd again | ||
this._push(node); | ||
// return the node | ||
return node; | ||
} | ||
else throw new Error('The node cannot be moved, the hash «'+hash+'» could not be found!'); | ||
} | ||
// return the node | ||
return node; | ||
} | ||
else throw new Error('The node cannot be moved, the hash «'+hash+'» could not be found!'); | ||
} | ||
/** | ||
* Moves an item so that its placed at the beginning of the list | ||
* | ||
* @param {*|Node} hash the hash of the item to move or the node | ||
* | ||
* @returns {Node} the node that was moved | ||
*/ | ||
, moveToEnd: function(hash) { | ||
var node; | ||
/** | ||
* Moves an item so that its placed at the beginning of the list | ||
* | ||
* @param {*|Node} hash the hash of the item to move or the node | ||
* | ||
* @returns {Node} the node that was moved | ||
*/ | ||
moveToEnd(hash) { | ||
let node; | ||
// check the input | ||
if (hash instanceof this.Node) hash = hash.hash; | ||
// check the input | ||
if (hash instanceof this.Node) hash = hash.hash; | ||
// the items need to exist | ||
if (this.has(hash)) { | ||
// remove the node | ||
node = this._removeNode(hash); | ||
// the items need to exist | ||
if (this.has(hash)) { | ||
// remove the node | ||
node = this._removeNode(hash); | ||
// addd again | ||
this._unshift(node); | ||
// addd again | ||
this._unshift(node); | ||
// return the node | ||
return node; | ||
} | ||
else throw new Error('The node cannot be moved, the hash «'+hash+'» could not be found!'); | ||
} | ||
// return the node | ||
return node; | ||
} | ||
else throw new Error('The node cannot be moved, the hash «'+hash+'» could not be found!'); | ||
} | ||
@@ -325,116 +322,116 @@ | ||
/** | ||
* add an element after another item in the list | ||
* | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* @param {*} hash the hash of the item this should be placed after | ||
* value | ||
* @returns {Node} the noded added | ||
*/ | ||
, addAfter: function(hash, value, afterHash) { | ||
/** | ||
* add an element after another item in the list | ||
* | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* @param {*} hash the hash of the item this should be placed after | ||
* value | ||
* @returns {Node} the noded added | ||
*/ | ||
addAfter(hash, value, afterHash) { | ||
var node = this._addAfter(hash, value, afterHash); | ||
let node = this._addAfter(hash, value, afterHash); | ||
// emit events | ||
this.emit('add', node.value); | ||
this.emit('addNode', node); | ||
this.emit('addAfter', node.value); | ||
this.emit('addAfterNode', node); | ||
// emit events | ||
this.emit('add', node.value); | ||
this.emit('addNode', node); | ||
this.emit('addAfter', node.value); | ||
this.emit('addAfterNode', node); | ||
return node; | ||
} | ||
return node; | ||
} | ||
/** | ||
* add an element after another item in the list | ||
* | ||
* @private | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* @param {*} hash the hash of the item this should be placed after | ||
* value | ||
* @returns {Linkd} this | ||
*/ | ||
, _addAfter: function(hash, value, afterHash) { | ||
var node, afterNode, existingNode = false; | ||
/** | ||
* add an element after another item in the list | ||
* | ||
* @private | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* @param {*} hash the hash of the item this should be placed after | ||
* value | ||
* @returns {Linkd} this | ||
*/ | ||
_addAfter(hash, value, afterHash) { | ||
let node, afterNode, existingNode = false; | ||
// clean up variables | ||
if (hash instanceof this.Node) { | ||
node = hash; | ||
hash = node.hash; | ||
afterHash = value; | ||
existingNode = true; | ||
} | ||
// clean up letiables | ||
if (hash instanceof this.Node) { | ||
node = hash; | ||
hash = node.hash; | ||
afterHash = value; | ||
existingNode = true; | ||
} | ||
// convert nodes to hashes | ||
if (afterHash instanceof this.Node) afterHash = afterHash.hash; | ||
// convert nodes to hashes | ||
if (afterHash instanceof this.Node) afterHash = afterHash.hash; | ||
// the node to insert after needds to exits! | ||
if (!this.has(afterHash)) throw new Error('Cannot add node after hash «'+afterHash+'», the node referenced node does not exist!'); | ||
else afterNode = this.map.get(afterHash); | ||
// the node to insert after needds to exits! | ||
if (!this.has(afterHash)) throw new Error('Cannot add node after hash «'+afterHash+'», the node referenced node does not exist!'); | ||
else afterNode = this.map.get(afterHash); | ||
// set up node | ||
if (existingNode) { | ||
node.nextNode = afterNode; | ||
node.previousNode = null; | ||
} | ||
else node = new this.Node(hash, value, null, afterNode); | ||
// set up node | ||
if (existingNode) { | ||
node.nextNode = afterNode; | ||
node.previousNode = null; | ||
} | ||
else node = new this.Node(hash, value, null, afterNode); | ||
// remove existing instances | ||
if (this.has(hash)) this._removeNode(hash); | ||
// remove existing instances | ||
if (this.has(hash)) this._removeNode(hash); | ||
// store the node | ||
this.map.set(hash, node); | ||
// store the node | ||
this.map.set(hash, node); | ||
// set this node between the afterNode and its previous node | ||
if (afterNode.previousNode) { | ||
afterNode.previousNode.nextNode = node; | ||
node.previousNode = afterNode.previousNode; | ||
} | ||
else { | ||
// this is the last node | ||
this.lastNode = node; | ||
} | ||
// set this node between the afterNode and its previous node | ||
if (afterNode.previousNode) { | ||
afterNode.previousNode.nextNode = node; | ||
node.previousNode = afterNode.previousNode; | ||
} | ||
else { | ||
// this is the last node | ||
this.lastNode = node; | ||
} | ||
// set link | ||
afterNode.previousNode = node; | ||
// set link | ||
afterNode.previousNode = node; | ||
// return this class for daisy chaning | ||
return node; | ||
} | ||
// return this class for daisy chaning | ||
return node; | ||
} | ||
/** | ||
* add an element before another item in the list | ||
* | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* @param {*} hash the hash of the item this should be placed before | ||
* value | ||
* @returns {Linkd} this | ||
*/ | ||
, addBefore: function(hash, value, beforeHash) { | ||
/** | ||
* add an element before another item in the list | ||
* | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* @param {*} hash the hash of the item this should be placed before | ||
* value | ||
* @returns {Linkd} this | ||
*/ | ||
addBefore(hash, value, beforeHash) { | ||
var node = this._addBefore(hash, value, beforeHash); | ||
let node = this._addBefore(hash, value, beforeHash); | ||
// emit events | ||
this.emit('add', node.value); | ||
this.emit('addNode', node); | ||
this.emit('addBefore', node.value); | ||
this.emit('addBeforeNode', node); | ||
// emit events | ||
this.emit('add', node.value); | ||
this.emit('addNode', node); | ||
this.emit('addBefore', node.value); | ||
this.emit('addBeforeNode', node); | ||
return this; | ||
} | ||
return this; | ||
} | ||
@@ -446,67 +443,67 @@ | ||
/** | ||
* add an element before another item in the list | ||
* @private | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* @param {*} hash the hash of the item this should be placed before | ||
* value | ||
* @returns {Linkd} this | ||
*/ | ||
, _addBefore: function(hash, value, beforeHash) { | ||
var node, beforeNode, existingNode = false; | ||
/** | ||
* add an element before another item in the list | ||
* @private | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* @param {*} hash the hash of the item this should be placed before | ||
* value | ||
* @returns {Linkd} this | ||
*/ | ||
_addBefore(hash, value, beforeHash) { | ||
let node, beforeNode, existingNode = false; | ||
// clean up variables | ||
if (hash instanceof this.Node) { | ||
node = hash; | ||
hash = node.hash; | ||
beforeHash = value; | ||
existingNode = true; | ||
} | ||
// clean up letiables | ||
if (hash instanceof this.Node) { | ||
node = hash; | ||
hash = node.hash; | ||
beforeHash = value; | ||
existingNode = true; | ||
} | ||
// convert nodes to hashes | ||
if (beforeHash instanceof this.Node) beforeHash = beforeHash.hash; | ||
// convert nodes to hashes | ||
if (beforeHash instanceof this.Node) beforeHash = beforeHash.hash; | ||
// the node to insert before needds to exits! | ||
if (!this.has(beforeHash)) throw new Error('Cannot add node before hash «'+beforeHash+'», the node referenced node does not exist!'); | ||
else beforeNode = this.map.get(beforeHash); | ||
// the node to insert before needds to exits! | ||
if (!this.has(beforeHash)) throw new Error('Cannot add node before hash «'+beforeHash+'», the node referenced node does not exist!'); | ||
else beforeNode = this.map.get(beforeHash); | ||
// set up node | ||
if (existingNode) { | ||
node.nextNode = null; | ||
node.previousNode = beforeNode; | ||
} | ||
else node = new this.Node(hash, value, beforeNode); | ||
// set up node | ||
if (existingNode) { | ||
node.nextNode = null; | ||
node.previousNode = beforeNode; | ||
} | ||
else node = new this.Node(hash, value, beforeNode); | ||
// remove existing instances | ||
if (this.has(hash)) this._removeNode(hash); | ||
// remove existing instances | ||
if (this.has(hash)) this._removeNode(hash); | ||
// store the node | ||
this.map.set(hash, node); | ||
// store the node | ||
this.map.set(hash, node); | ||
// set this node between the beforeNode and its previous node | ||
if (beforeNode.nextNode) { | ||
beforeNode.nextNode.previousNode = node; | ||
node.nextNode = beforeNode.nextNode; | ||
} | ||
else { | ||
// this is the first node | ||
this.firstNode = node; | ||
} | ||
// set this node between the beforeNode and its previous node | ||
if (beforeNode.nextNode) { | ||
beforeNode.nextNode.previousNode = node; | ||
node.nextNode = beforeNode.nextNode; | ||
} | ||
else { | ||
// this is the first node | ||
this.firstNode = node; | ||
} | ||
// set link | ||
beforeNode.nextNode = node; | ||
// set link | ||
beforeNode.nextNode = node; | ||
// return this class for daisy chaning | ||
return node; | ||
} | ||
// return this class for daisy chaning | ||
return node; | ||
} | ||
@@ -516,22 +513,22 @@ | ||
/** | ||
* add an element to the beginning of the list | ||
* | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* value | ||
* @returns {Node} the created node | ||
*/ | ||
, push: function(hash, value) { | ||
/** | ||
* add an element to the beginning of the list | ||
* | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* value | ||
* @returns {Node} the created node | ||
*/ | ||
push(hash, value) { | ||
var node = this._push(hash, value); | ||
let node = this._push(hash, value); | ||
// emit events | ||
this.emit('add', node.value); | ||
this.emit('addNode', node); | ||
this.emit('push', node.value); | ||
this.emit('pushNode', node); | ||
// emit events | ||
this.emit('add', node.value); | ||
this.emit('addNode', node); | ||
this.emit('push', node.value); | ||
this.emit('pushNode', node); | ||
return node; | ||
} | ||
return node; | ||
} | ||
@@ -541,39 +538,39 @@ | ||
/** | ||
* add an element to the beginning of the list | ||
* | ||
* @private | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* value | ||
* @returns {Node} the created node | ||
*/ | ||
, _push: function(hash, value) { | ||
var node; | ||
/** | ||
* add an element to the beginning of the list | ||
* | ||
* @private | ||
* @param {*|node} hash a hash identifying an item, may be of any type or a node | ||
* @param {*} the value to store. undefiend is the only unacceptable | ||
* value | ||
* @returns {Node} the created node | ||
*/ | ||
_push(hash, value) { | ||
let node; | ||
// set up node | ||
if (hash instanceof this.Node) { | ||
node = hash; | ||
hash = node.hash; | ||
// set up node | ||
if (hash instanceof this.Node) { | ||
node = hash; | ||
hash = node.hash; | ||
node.nextNode = null; | ||
node.previousNode = this.firstNode; | ||
} | ||
else node = new this.Node(hash, value, this.firstNode); | ||
node.nextNode = null; | ||
node.previousNode = this.firstNode; | ||
} | ||
else node = new this.Node(hash, value, this.firstNode); | ||
// remove existing instances | ||
if (this.has(hash)) this._removeNode(hash); | ||
// remove existing instances | ||
if (this.has(hash)) this._removeNode(hash); | ||
// store the node | ||
this.map.set(hash, node); | ||
// store the node | ||
this.map.set(hash, node); | ||
// set myself as nextnode | ||
if (this.firstNode) this.firstNode.nextNode = node; | ||
// set myself as nextnode | ||
if (this.firstNode) this.firstNode.nextNode = node; | ||
// set as first node | ||
this.firstNode = node; | ||
// set as first node | ||
this.firstNode = node; | ||
@@ -585,5 +582,5 @@ | ||
// return this class for daisy chaning | ||
return node; | ||
} | ||
// return this class for daisy chaning | ||
return node; | ||
} | ||
@@ -593,22 +590,22 @@ | ||
/** | ||
* add an element to the end of the list | ||
* | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @param {any} the value to store. undefiend is the only unacceptable | ||
* value | ||
* @returns {Node} the created node | ||
*/ | ||
, unshift: function(hash, value) { | ||
/** | ||
* add an element to the end of the list | ||
* | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @param {any} the value to store. undefiend is the only unacceptable | ||
* value | ||
* @returns {Node} the created node | ||
*/ | ||
unshift(hash, value) { | ||
var node = this._unshift(hash, value); | ||
let node = this._unshift(hash, value); | ||
// emit events | ||
this.emit('add', node.value); | ||
this.emit('addNode', node); | ||
this.emit('unshift', node.value); | ||
this.emit('unshiftNode', node); | ||
// emit events | ||
this.emit('add', node.value); | ||
this.emit('addNode', node); | ||
this.emit('unshift', node.value); | ||
this.emit('unshiftNode', node); | ||
return node; | ||
} | ||
return node; | ||
} | ||
@@ -619,39 +616,39 @@ | ||
/** | ||
* add an element to the end of the list | ||
* | ||
* @private | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @param {any} the value to store. undefiend is the only unacceptable | ||
* value | ||
* @returns {Node} the created node | ||
*/ | ||
, _unshift: function(hash, value) { | ||
var node; | ||
/** | ||
* add an element to the end of the list | ||
* | ||
* @private | ||
* @param {*} hash a hash identifying an item, may be of any type | ||
* @param {any} the value to store. undefiend is the only unacceptable | ||
* value | ||
* @returns {Node} the created node | ||
*/ | ||
_unshift(hash, value) { | ||
let node; | ||
// set up node | ||
if (hash instanceof this.Node) { | ||
node = hash; | ||
hash = node.hash; | ||
// set up node | ||
if (hash instanceof this.Node) { | ||
node = hash; | ||
hash = node.hash; | ||
node.nextNode = this.lastNode; | ||
node.previousNode = null; | ||
} | ||
else node = new this.Node(hash, value, null, this.lastNode); | ||
node.nextNode = this.lastNode; | ||
node.previousNode = null; | ||
} | ||
else node = new this.Node(hash, value, null, this.lastNode); | ||
// remove existing instances | ||
if (this.has(hash)) this._removeNode(hash); | ||
// remove existing instances | ||
if (this.has(hash)) this._removeNode(hash); | ||
// store the node | ||
this.map.set(hash, node); | ||
// store the node | ||
this.map.set(hash, node); | ||
// set myself as nextnode | ||
if (this.lastNode) this.lastNode.previousNode = node; | ||
// set myself as nextnode | ||
if (this.lastNode) this.lastNode.previousNode = node; | ||
// set as first node | ||
this.lastNode = node; | ||
// set as first node | ||
this.lastNode = node; | ||
@@ -662,5 +659,5 @@ | ||
// return this class for daisy chaning | ||
return node; | ||
} | ||
// return this class for daisy chaning | ||
return node; | ||
} | ||
@@ -670,37 +667,37 @@ | ||
/** | ||
* remove an element from the beginning of the list | ||
* | ||
* @returns {Node} the removed node | ||
*/ | ||
, popNode: function() { | ||
var node; | ||
/** | ||
* remove an element from the beginning of the list | ||
* | ||
* @returns {Node} the removed node | ||
*/ | ||
popNode() { | ||
let node; | ||
if (this.firstNode) { | ||
node = this._removeNode(this.firstNode.hash); | ||
if (this.firstNode) { | ||
node = this._removeNode(this.firstNode.hash); | ||
if (node) { | ||
this.emit('remove', node.value); | ||
this.emit('removeNode', node); | ||
this.emit('pop', node.value); | ||
this.emit('popNode', node); | ||
} | ||
if (node) { | ||
this.emit('remove', node.value); | ||
this.emit('removeNode', node); | ||
this.emit('pop', node.value); | ||
this.emit('popNode', node); | ||
} | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
/** | ||
* remove an element from the beginning of the list | ||
* | ||
* @returns {*} the removed item | ||
*/ | ||
, pop: function() { | ||
var node = this.popNode(); | ||
/** | ||
* remove an element from the beginning of the list | ||
* | ||
* @returns {*} the removed item | ||
*/ | ||
pop() { | ||
let node = this.popNode(); | ||
return node ? node.value : node; | ||
} | ||
return node ? node.value : node; | ||
} | ||
@@ -710,24 +707,24 @@ | ||
/** | ||
* remove an element from the beginning of the list | ||
* | ||
* @returns {Node} the removed item | ||
*/ | ||
, shiftNode: function() { | ||
var node; | ||
/** | ||
* remove an element from the beginning of the list | ||
* | ||
* @returns {Node} the removed item | ||
*/ | ||
shiftNode() { | ||
let node; | ||
if (this.lastNode) { | ||
node = this._removeNode(this.lastNode.hash); | ||
if (this.lastNode) { | ||
node = this._removeNode(this.lastNode.hash); | ||
if (node) { | ||
this.emit('remove', node.value); | ||
this.emit('removeNode', node); | ||
this.emit('shift', node.value); | ||
this.emit('shiftNode', node); | ||
} | ||
if (node) { | ||
this.emit('remove', node.value); | ||
this.emit('removeNode', node); | ||
this.emit('shift', node.value); | ||
this.emit('shiftNode', node); | ||
} | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
@@ -737,12 +734,12 @@ | ||
/** | ||
* remove an element from the beginning of the list | ||
* | ||
* @returns {*} the removed item | ||
*/ | ||
, shift: function() { | ||
var node = this.shiftNode(); | ||
/** | ||
* remove an element from the beginning of the list | ||
* | ||
* @returns {*} the removed item | ||
*/ | ||
shift() { | ||
let node = this.shiftNode(); | ||
return node ? node.value : node; | ||
} | ||
return node ? node.value : node; | ||
} | ||
@@ -754,21 +751,21 @@ | ||
/** | ||
* remove an element from the list | ||
* | ||
* @param {*|Node} hash a hash identifying an item, may be of any type or node | ||
* @returns {Node} the removed node | ||
*/ | ||
, removeNode: function(hash) { | ||
var node; | ||
/** | ||
* remove an element from the list | ||
* | ||
* @param {*|Node} hash a hash identifying an item, may be of any type or node | ||
* @returns {Node} the removed node | ||
*/ | ||
removeNode(hash) { | ||
let node; | ||
if (hash instanceof this.Node) node = this._removeNode(hash.hash); | ||
else node = this._removeNode(hash); | ||
if (hash instanceof this.Node) node = this._removeNode(hash.hash); | ||
else node = this._removeNode(hash); | ||
if (node) { | ||
this.emit('remove', node.value); | ||
this.emit('removeNode', node); | ||
} | ||
if (node) { | ||
this.emit('remove', node.value); | ||
this.emit('removeNode', node); | ||
} | ||
return node; | ||
} | ||
return node; | ||
} | ||
@@ -779,13 +776,13 @@ | ||
/** | ||
* remove an element from the list | ||
* | ||
* @param {*|Node} hash a hash identifying an item, may be of any type or node | ||
* @returns {*} the removed item | ||
*/ | ||
, remove: function(hash) { | ||
var node = this.removeNode(hash); | ||
/** | ||
* remove an element from the list | ||
* | ||
* @param {*|Node} hash a hash identifying an item, may be of any type or node | ||
* @returns {*} the removed item | ||
*/ | ||
remove(hash) { | ||
let node = this.removeNode(hash); | ||
return node ? node.value : node; | ||
} | ||
return node ? node.value : node; | ||
} | ||
@@ -796,10 +793,10 @@ | ||
/** | ||
* returns all the hashes that are stored in the list | ||
* | ||
* @returns {Iterable*} with all hashes | ||
*/ | ||
, keys: function() { | ||
return this.map.keys(); | ||
} | ||
/** | ||
* returns all the hashes that are stored in the list | ||
* | ||
* @returns {Iterable*} with all hashes | ||
*/ | ||
keys() { | ||
return this.map.keys(); | ||
} | ||
@@ -809,18 +806,18 @@ | ||
/** | ||
* clears all values from the linked list, re-initilizes | ||
* the class isntance | ||
*/ | ||
, clear: function() { | ||
/** | ||
* clears all values from the linked list, re-initilizes | ||
* the class isntance | ||
*/ | ||
clear() { | ||
// create a new map | ||
Class.define(this, 'map', Class(new Map()).Writable()); | ||
// create a new map | ||
Class.define(this, 'map', Class(new Map()).Writable()); | ||
// clear nodes | ||
this.lastNode = null; | ||
this.firstNode = null; | ||
// clear nodes | ||
this.lastNode = null; | ||
this.firstNode = null; | ||
this.emit('clear'); | ||
} | ||
this.emit('clear'); | ||
} | ||
@@ -830,51 +827,50 @@ | ||
/** | ||
* removes a node from the lsit | ||
* | ||
* @private | ||
* @param {*} hash the hash of the node to remove | ||
* @returns {Node} node | ||
*/ | ||
, _removeNode: function(hash) { | ||
var toBeRemovedNode = this.map.has(hash) ? this.map.get(hash) : null; | ||
/** | ||
* removes a node from the lsit | ||
* | ||
* @private | ||
* @param {*} hash the hash of the node to remove | ||
* @returns {Node} node | ||
*/ | ||
_removeNode(hash) { | ||
let toBeRemovedNode = this.map.has(hash) ? this.map.get(hash) : null; | ||
if (toBeRemovedNode) { | ||
if (toBeRemovedNode.nextNode) { | ||
if (toBeRemovedNode.previousNode) { | ||
// this node is in between of two nodes | ||
toBeRemovedNode.nextNode.previousNode = toBeRemovedNode.previousNode; | ||
toBeRemovedNode.previousNode.nextNode = toBeRemovedNode.nextNode; | ||
} | ||
else { | ||
// this is the last node | ||
toBeRemovedNode.nextNode.previousNode = null; | ||
this.lastNode = toBeRemovedNode.nextNode; | ||
} | ||
} | ||
else { | ||
if (toBeRemovedNode.previousNode) { | ||
// this is the first node | ||
toBeRemovedNode.previousNode.nextNode = null; | ||
this.firstNode = toBeRemovedNode.previousNode; | ||
} | ||
else { | ||
// this was the last node | ||
this.lastNode = null; | ||
this.firstNode = null; | ||
} | ||
} | ||
if (toBeRemovedNode) { | ||
if (toBeRemovedNode.nextNode) { | ||
if (toBeRemovedNode.previousNode) { | ||
// this node is in between of two nodes | ||
toBeRemovedNode.nextNode.previousNode = toBeRemovedNode.previousNode; | ||
toBeRemovedNode.previousNode.nextNode = toBeRemovedNode.nextNode; | ||
} | ||
else { | ||
// this is the last node | ||
toBeRemovedNode.nextNode.previousNode = null; | ||
this.lastNode = toBeRemovedNode.nextNode; | ||
} | ||
} | ||
else { | ||
if (toBeRemovedNode.previousNode) { | ||
// this is the first node | ||
toBeRemovedNode.previousNode.nextNode = null; | ||
this.firstNode = toBeRemovedNode.previousNode; | ||
} | ||
else { | ||
// this was the last node | ||
this.lastNode = null; | ||
this.firstNode = null; | ||
} | ||
} | ||
// remove from map | ||
this.map.delete(hash); | ||
// remove from map | ||
this.map.delete(hash); | ||
// remove links | ||
toBeRemovedNode.nextNode = null; | ||
toBeRemovedNode.previousNode = null; | ||
// remove links | ||
toBeRemovedNode.nextNode = null; | ||
toBeRemovedNode.previousNode = null; | ||
// return the deleted node | ||
return toBeRemovedNode; | ||
} | ||
else throw new Error('Cannot remove the node «'+(hash && hash.toString ? hash.toString() : hash)+'», the node doesn\'t exist!'); | ||
} | ||
}; | ||
// return the deleted node | ||
return toBeRemovedNode; | ||
} | ||
else throw new Error('Cannot remove the node «'+(hash && hash.toString ? hash.toString() : hash)+'», the node doesn\'t exist!'); | ||
} | ||
@@ -886,30 +882,20 @@ | ||
[Symbol.iterator]() { | ||
let currentNode = this.firstNode; | ||
/** | ||
* returns an ES6 iterator object | ||
* | ||
* @returns {Iterator} Iterator object | ||
*/ | ||
classDefinition[Symbol.iterator] = function() { | ||
var currentNode = this.firstNode; | ||
return { | ||
next: function() { | ||
let returnNode; | ||
return { | ||
next: function() { | ||
var returnNode; | ||
if (currentNode) { | ||
returnNode = currentNode; | ||
currentNode = currentNode.previousNode || null; | ||
if (currentNode) { | ||
returnNode = currentNode; | ||
currentNode = currentNode.previousNode || null; | ||
return {value: returnNode.value, done: false}; | ||
} | ||
else return {done: true}; | ||
} | ||
}; | ||
}; | ||
// make a class | ||
module.exports = new Class(classDefinition); | ||
return {value: returnNode.value, done: false}; | ||
} | ||
else return {done: true}; | ||
} | ||
}; | ||
} | ||
}; | ||
}(); |
130
lib/Node.js
!function() { | ||
'use strict'; | ||
var Class = require('ee-class') | ||
, log = require('ee-log') | ||
, EventEmitter = require('ee-event-emitter'); | ||
const log = require('ee-log'); | ||
const EventEmitter = require('ee-event-emitter'); | ||
module.exports = new Class({ | ||
inherits: EventEmitter | ||
module.exports = class Node extends EventEmitter { | ||
, identifier: Symbol('Linkd.Node') | ||
/** | ||
* initilize the linked list node | ||
* | ||
* @param {Item} customType a custom item implementation used for storing | ||
* ittems | ||
*/ | ||
constructor(hash, value, previousNode, nextNode) { | ||
super(); | ||
/** | ||
* initilize the linked list node | ||
* | ||
* @param {Item} customType a custom item implementation used for storing | ||
* ittems | ||
*/ | ||
, init: function(hash, value, previousNode, nextNode) { | ||
this.identifier = Symbol('Linkd.Node'); | ||
// we may need the hash later on | ||
this.hash = hash; | ||
// we may need the hash later on | ||
this.hash = hash; | ||
@@ -35,65 +35,65 @@ // save the value | ||
Class.define(this, 'nextNode', Class(nextNode || null).Writable().Configurable().Enumerable()); | ||
} | ||
} | ||
/** | ||
* returns true if there is a next node in the list | ||
* | ||
* @returns {bool} true if there is a next node in the list | ||
*/ | ||
, hasNext: function() { | ||
return !!this.nextNode; | ||
} | ||
/** | ||
* returns true if there is a next node in the list | ||
* | ||
* @returns {bool} true if there is a next node in the list | ||
*/ | ||
hasNext() { | ||
return !!this.nextNode; | ||
} | ||
/** | ||
* returns true if there is a previous node in the list | ||
* | ||
* @returns {bool} true if there is a previous node in the list | ||
*/ | ||
, hasPrevious: function() { | ||
return !!this.previousNode; | ||
} | ||
/** | ||
* returns true if there is a previous node in the list | ||
* | ||
* @returns {bool} true if there is a previous node in the list | ||
*/ | ||
hasPrevious() { | ||
return !!this.previousNode; | ||
} | ||
/** | ||
* returns true if this is the last node in the list | ||
* | ||
* @returns {bool} true if this is the last node in the list | ||
*/ | ||
, isLast: function() { | ||
return !this.previousNode; | ||
} | ||
/** | ||
* returns true if this is the last node in the list | ||
* | ||
* @returns {bool} true if this is the last node in the list | ||
*/ | ||
isLast() { | ||
return !this.previousNode; | ||
} | ||
/** | ||
* returns true if this is the first node in the list | ||
* | ||
* @returns {bool} true if this is the first node in the list | ||
*/ | ||
, isFrist: function() { | ||
return !this.nextNode; | ||
} | ||
/** | ||
* returns true if this is the first node in the list | ||
* | ||
* @returns {bool} true if this is the first node in the list | ||
*/ | ||
isFrist() { | ||
return !this.nextNode; | ||
} | ||
/** | ||
* returns the next node or null | ||
* | ||
* @returns {Node|null} the next node or null | ||
*/ | ||
, getNext: function() { | ||
return this.nextNode || null; | ||
} | ||
/** | ||
* returns the next node or null | ||
* | ||
* @returns {Node|null} the next node or null | ||
*/ | ||
getNext() { | ||
return this.nextNode || null; | ||
} | ||
/** | ||
* returns the previous node or null | ||
* | ||
* @returns {Node|null} the previous node or null | ||
*/ | ||
, getPrevious: function() { | ||
return this.previousNode || null; | ||
} | ||
}); | ||
/** | ||
* returns the previous node or null | ||
* | ||
* @returns {Node|null} the previous node or null | ||
*/ | ||
getPrevious() { | ||
return this.previousNode || null; | ||
} | ||
}; | ||
}(); |
{ | ||
"name" : "linkd" | ||
, "description" : "A fast and extendable doubly linked list" | ||
, "version" : "1.0.1" | ||
, "version" : "2.0.0" | ||
, "homepage" : "https://github.com/eventEmitter/linkd" | ||
@@ -13,3 +13,3 @@ , "author" : "Michael van der Weg <michael@joinbox.com> (http://joinbox.com/)" | ||
, "engines": { | ||
"node" : ">=v4" | ||
"node" : ">=v6" | ||
} | ||
@@ -20,8 +20,7 @@ , "bugs": { | ||
, "dependencies": { | ||
"ee-class" : "1.x" | ||
, "ee-event-emitter" : "0.3.x" | ||
, "ee-log" : "0.3.x" | ||
"ee-event-emitter" : "0.3.x" | ||
, "ee-log" : "1.x" | ||
} | ||
, "devDependencies": { | ||
"mocha" : "2.x" | ||
"mocha" : "3.x" | ||
} | ||
@@ -28,0 +27,0 @@ , "optionalDependencies": {} |
@@ -27,2 +27,6 @@ # linkd | ||
Attention: many methods work have similar names as those on native arrays but may actually work not the same: the push method on an array adds an element to the end of the array, he push method on the linkd list adds an element to the beginning of the list. | ||
### Method Overview | ||
@@ -29,0 +33,0 @@ |
@@ -51,2 +51,20 @@ | ||
it('should corrently save unshifted and push nodes', function() { | ||
var list = new LinkedList() | ||
, arr = []; | ||
list.push(10, 'x'); | ||
list.unshift(1, 'a'); | ||
list.unshift(2, 'b'); | ||
list.unshift(3, 'c'); | ||
list.unshift(4, 'd'); | ||
list.push(11, 'y'); | ||
list.unshift(5, 'e'); | ||
for (var x of list) arr.push(x); | ||
assert.deepEqual(arr, ['e', 'd', 'c', 'b', 'a', 'x', 'y'].reverse()); | ||
}); | ||
it('should corrently save addAfter nodes', function() { | ||
@@ -53,0 +71,0 @@ var list = new LinkedList() |
Sorry, the diff of this file is not supported yet
43773
2
898
317
+ Addedee-log@1.1.0(transitive)
- Removedee-class@1.x
- Removedee-argv@0.1.4(transitive)
- Removedee-log@0.3.11(transitive)
Updatedee-log@1.x