You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					262 lines
				
				4.6 KiB
			
		
		
			
		
	
	
					262 lines
				
				4.6 KiB
			| 
											3 years ago
										 | /** | ||
|  |  * Mnemonist Linked List | ||
|  |  * ====================== | ||
|  |  * | ||
|  |  * Singly linked list implementation. Uses raw JavaScript objects as nodes | ||
|  |  * as benchmarks proved it was the fastest thing to do. | ||
|  |  */ | ||
|  | var Iterator = require('obliterator/iterator'), | ||
|  |     forEach = require('obliterator/foreach'); | ||
|  | 
 | ||
|  | /** | ||
|  |  * Linked List. | ||
|  |  * | ||
|  |  * @constructor | ||
|  |  */ | ||
|  | function LinkedList() { | ||
|  |   this.clear(); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to clear the list. | ||
|  |  * | ||
|  |  * @return {undefined} | ||
|  |  */ | ||
|  | LinkedList.prototype.clear = function() { | ||
|  | 
 | ||
|  |   // Properties
 | ||
|  |   this.head = null; | ||
|  |   this.tail = null; | ||
|  |   this.size = 0; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to get the first item of the list. | ||
|  |  * | ||
|  |  * @return {any} | ||
|  |  */ | ||
|  | LinkedList.prototype.first = function() { | ||
|  |   return this.head ? this.head.item : undefined; | ||
|  | }; | ||
|  | LinkedList.prototype.peek = LinkedList.prototype.first; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to get the last item of the list. | ||
|  |  * | ||
|  |  * @return {any} | ||
|  |  */ | ||
|  | LinkedList.prototype.last = function() { | ||
|  |   return this.tail ? this.tail.item : undefined; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to add an item at the end of the list. | ||
|  |  * | ||
|  |  * @param  {any}    item - The item to add. | ||
|  |  * @return {number} | ||
|  |  */ | ||
|  | LinkedList.prototype.push = function(item) { | ||
|  |   var node = {item: item, next: null}; | ||
|  | 
 | ||
|  |   if (!this.head) { | ||
|  |     this.head = node; | ||
|  |     this.tail = node; | ||
|  |   } | ||
|  |   else { | ||
|  |     this.tail.next = node; | ||
|  |     this.tail = node; | ||
|  |   } | ||
|  | 
 | ||
|  |   this.size++; | ||
|  | 
 | ||
|  |   return this.size; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to add an item at the beginning of the list. | ||
|  |  * | ||
|  |  * @param  {any}    item - The item to add. | ||
|  |  * @return {number} | ||
|  |  */ | ||
|  | LinkedList.prototype.unshift = function(item) { | ||
|  |   var node = {item: item, next: null}; | ||
|  | 
 | ||
|  |   if (!this.head) { | ||
|  |     this.head = node; | ||
|  |     this.tail = node; | ||
|  |   } | ||
|  |   else { | ||
|  |     if (!this.head.next) | ||
|  |       this.tail = this.head; | ||
|  |     node.next = this.head; | ||
|  |     this.head = node; | ||
|  |   } | ||
|  | 
 | ||
|  |   this.size++; | ||
|  | 
 | ||
|  |   return this.size; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to retrieve & remove the first item of the list. | ||
|  |  * | ||
|  |  * @return {any} | ||
|  |  */ | ||
|  | LinkedList.prototype.shift = function() { | ||
|  |   if (!this.size) | ||
|  |     return undefined; | ||
|  | 
 | ||
|  |   var node = this.head; | ||
|  | 
 | ||
|  |   this.head = node.next; | ||
|  |   this.size--; | ||
|  | 
 | ||
|  |   return node.item; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to iterate over the list. | ||
|  |  * | ||
|  |  * @param  {function}  callback - Function to call for each item. | ||
|  |  * @param  {object}    scope    - Optional scope. | ||
|  |  * @return {undefined} | ||
|  |  */ | ||
|  | LinkedList.prototype.forEach = function(callback, scope) { | ||
|  |   if (!this.size) | ||
|  |     return; | ||
|  | 
 | ||
|  |   scope = arguments.length > 1 ? scope : this; | ||
|  | 
 | ||
|  |   var n = this.head, | ||
|  |       i = 0; | ||
|  | 
 | ||
|  |   while (n) { | ||
|  |     callback.call(scope, n.item, i, this); | ||
|  |     n = n.next; | ||
|  |     i++; | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to convert the list into an array. | ||
|  |  * | ||
|  |  * @return {array} | ||
|  |  */ | ||
|  | LinkedList.prototype.toArray = function() { | ||
|  |   if (!this.size) | ||
|  |     return []; | ||
|  | 
 | ||
|  |   var array = new Array(this.size); | ||
|  | 
 | ||
|  |   for (var i = 0, l = this.size, n = this.head; i < l; i++) { | ||
|  |     array[i] = n.item; | ||
|  |     n = n.next; | ||
|  |   } | ||
|  | 
 | ||
|  |   return array; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to create an iterator over a list's values. | ||
|  |  * | ||
|  |  * @return {Iterator} | ||
|  |  */ | ||
|  | LinkedList.prototype.values = function() { | ||
|  |   var n = this.head; | ||
|  | 
 | ||
|  |   return new Iterator(function() { | ||
|  |     if (!n) | ||
|  |       return { | ||
|  |         done: true | ||
|  |       }; | ||
|  | 
 | ||
|  |     var value = n.item; | ||
|  |     n = n.next; | ||
|  | 
 | ||
|  |     return { | ||
|  |       value: value, | ||
|  |       done: false | ||
|  |     }; | ||
|  |   }); | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Method used to create an iterator over a list's entries. | ||
|  |  * | ||
|  |  * @return {Iterator} | ||
|  |  */ | ||
|  | LinkedList.prototype.entries = function() { | ||
|  |   var n = this.head, | ||
|  |       i = 0; | ||
|  | 
 | ||
|  |   return new Iterator(function() { | ||
|  |     if (!n) | ||
|  |       return { | ||
|  |         done: true | ||
|  |       }; | ||
|  | 
 | ||
|  |     var value = n.item; | ||
|  |     n = n.next; | ||
|  |     i++; | ||
|  | 
 | ||
|  |     return { | ||
|  |       value: [i - 1, value], | ||
|  |       done: false | ||
|  |     }; | ||
|  |   }); | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Attaching the #.values method to Symbol.iterator if possible. | ||
|  |  */ | ||
|  | if (typeof Symbol !== 'undefined') | ||
|  |   LinkedList.prototype[Symbol.iterator] = LinkedList.prototype.values; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Convenience known methods. | ||
|  |  */ | ||
|  | LinkedList.prototype.toString = function() { | ||
|  |   return this.toArray().join(','); | ||
|  | }; | ||
|  | 
 | ||
|  | LinkedList.prototype.toJSON = function() { | ||
|  |   return this.toArray(); | ||
|  | }; | ||
|  | 
 | ||
|  | LinkedList.prototype.inspect = function() { | ||
|  |   var array = this.toArray(); | ||
|  | 
 | ||
|  |   // Trick so that node displays the name of the constructor
 | ||
|  |   Object.defineProperty(array, 'constructor', { | ||
|  |     value: LinkedList, | ||
|  |     enumerable: false | ||
|  |   }); | ||
|  | 
 | ||
|  |   return array; | ||
|  | }; | ||
|  | 
 | ||
|  | if (typeof Symbol !== 'undefined') | ||
|  |   LinkedList.prototype[Symbol.for('nodejs.util.inspect.custom')] = LinkedList.prototype.inspect; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Static @.from function taking an arbitrary iterable & converting it into | ||
|  |  * a list. | ||
|  |  * | ||
|  |  * @param  {Iterable} iterable   - Target iterable. | ||
|  |  * @return {LinkedList} | ||
|  |  */ | ||
|  | LinkedList.from = function(iterable) { | ||
|  |   var list = new LinkedList(); | ||
|  | 
 | ||
|  |   forEach(iterable, function(value) { | ||
|  |     list.push(value); | ||
|  |   }); | ||
|  | 
 | ||
|  |   return list; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Exporting. | ||
|  |  */ | ||
|  | module.exports = LinkedList; |