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.
		
		
		
		
		
			
		
			
				
					
					
						
							168 lines
						
					
					
						
							3.4 KiB
						
					
					
				
			
		
		
	
	
							168 lines
						
					
					
						
							3.4 KiB
						
					
					
				| /**
 | |
|  * Mnemonist Trie
 | |
|  * ===============
 | |
|  *
 | |
|  * JavaScript Trie implementation based upon plain objects. As such this
 | |
|  * structure is more a convenience building upon the trie's advantages than
 | |
|  * a real performant alternative to already existing structures.
 | |
|  *
 | |
|  * Note that the Trie is based upon the TrieMap since the underlying machine
 | |
|  * is the very same. The Trie just does not let you set values and only
 | |
|  * considers the existence of the given prefixes.
 | |
|  */
 | |
| var forEach = require('obliterator/foreach'),
 | |
|     TrieMap = require('./trie-map.js');
 | |
| 
 | |
| /**
 | |
|  * Constants.
 | |
|  */
 | |
| var SENTINEL = String.fromCharCode(0);
 | |
| 
 | |
| /**
 | |
|  * Trie.
 | |
|  *
 | |
|  * @constructor
 | |
|  */
 | |
| function Trie(Token) {
 | |
|   this.mode = Token === Array ? 'array' : 'string';
 | |
|   this.clear();
 | |
| }
 | |
| 
 | |
| // Re-using TrieMap's prototype
 | |
| for (var methodName in TrieMap.prototype)
 | |
|   Trie.prototype[methodName] = TrieMap.prototype[methodName];
 | |
| 
 | |
| // Dropping irrelevant methods
 | |
| delete Trie.prototype.set;
 | |
| delete Trie.prototype.get;
 | |
| delete Trie.prototype.values;
 | |
| delete Trie.prototype.entries;
 | |
| 
 | |
| /**
 | |
|  * Method used to add the given prefix to the trie.
 | |
|  *
 | |
|  * @param  {string|array} prefix - Prefix to follow.
 | |
|  * @return {TrieMap}
 | |
|  */
 | |
| Trie.prototype.add = function(prefix) {
 | |
|   var node = this.root,
 | |
|       token;
 | |
| 
 | |
|   for (var i = 0, l = prefix.length; i < l; i++) {
 | |
|     token = prefix[i];
 | |
| 
 | |
|     node = node[token] || (node[token] = {});
 | |
|   }
 | |
| 
 | |
|   // Do we need to increase size?
 | |
|   if (!(SENTINEL in node))
 | |
|     this.size++;
 | |
| 
 | |
|   node[SENTINEL] = true;
 | |
| 
 | |
|   return this;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Method used to retrieve every item in the trie with the given prefix.
 | |
|  *
 | |
|  * @param  {string|array} prefix - Prefix to query.
 | |
|  * @return {array}
 | |
|  */
 | |
| Trie.prototype.find = function(prefix) {
 | |
|   var isString = typeof prefix === 'string';
 | |
| 
 | |
|   var node = this.root,
 | |
|       matches = [],
 | |
|       token,
 | |
|       i,
 | |
|       l;
 | |
| 
 | |
|   for (i = 0, l = prefix.length; i < l; i++) {
 | |
|     token = prefix[i];
 | |
|     node = node[token];
 | |
| 
 | |
|     if (typeof node === 'undefined')
 | |
|       return matches;
 | |
|   }
 | |
| 
 | |
|   // Performing DFS from prefix
 | |
|   var nodeStack = [node],
 | |
|       prefixStack = [prefix],
 | |
|       k;
 | |
| 
 | |
|   while (nodeStack.length) {
 | |
|     prefix = prefixStack.pop();
 | |
|     node = nodeStack.pop();
 | |
| 
 | |
|     for (k in node) {
 | |
|       if (k === SENTINEL) {
 | |
|         matches.push(prefix);
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       nodeStack.push(node[k]);
 | |
|       prefixStack.push(isString ? prefix + k : prefix.concat(k));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return matches;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Attaching the #.keys method to Symbol.iterator if possible.
 | |
|  */
 | |
| if (typeof Symbol !== 'undefined')
 | |
|   Trie.prototype[Symbol.iterator] = Trie.prototype.keys;
 | |
| 
 | |
| /**
 | |
|  * Convenience known methods.
 | |
|  */
 | |
| Trie.prototype.inspect = function() {
 | |
|   var proxy = new Set();
 | |
| 
 | |
|   var iterator = this.keys(),
 | |
|       step;
 | |
| 
 | |
|   while ((step = iterator.next(), !step.done))
 | |
|     proxy.add(step.value);
 | |
| 
 | |
|   // Trick so that node displays the name of the constructor
 | |
|   Object.defineProperty(proxy, 'constructor', {
 | |
|     value: Trie,
 | |
|     enumerable: false
 | |
|   });
 | |
| 
 | |
|   return proxy;
 | |
| };
 | |
| 
 | |
| if (typeof Symbol !== 'undefined')
 | |
|   Trie.prototype[Symbol.for('nodejs.util.inspect.custom')] = Trie.prototype.inspect;
 | |
| 
 | |
| Trie.prototype.toJSON = function() {
 | |
|   return this.root;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Static @.from function taking an arbitrary iterable & converting it into
 | |
|  * a trie.
 | |
|  *
 | |
|  * @param  {Iterable} iterable   - Target iterable.
 | |
|  * @return {Trie}
 | |
|  */
 | |
| Trie.from = function(iterable) {
 | |
|   var trie = new Trie();
 | |
| 
 | |
|   forEach(iterable, function(value) {
 | |
|     trie.add(value);
 | |
|   });
 | |
| 
 | |
|   return trie;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Exporting.
 | |
|  */
 | |
| Trie.SENTINEL = SENTINEL;
 | |
| module.exports = Trie;
 |