@clarketm/supertrie
Version:
Trie with superpowers! 💪
475 lines (402 loc) • 11 kB
JavaScript
/**
* Copyright (c) 2018, Travis Clarke
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.Trie = {})));
}(this, (function (exports) { 'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
/**
*
* @module super/trie
*/
var SpecialChar = {
ROOT: "√"
};
/**
*
* TrieNode
*
* @public
*
*/
var TrieNode = function () {
/** @private */
/** @private */
/** @private */
/**
* @public
*
* @desc Construct a TrieNode
*
* @param {character} char - node character value
*/
function TrieNode() {
var char = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : SpecialChar.ROOT;
classCallCheck(this, TrieNode);
this._char = char;
this._isCompleteWord = false;
this._children = new Map();
}
/**
* @public
*
* @desc Get children count of the node
*
* @returns {number} child count
*/
// INFO: count or size?
createClass(TrieNode, [{
key: "has",
/**
* @public
*
* @desc Check if node has a specific character as a child
*
* @param {character} char - character to check
* @returns {boolean} node has child
*/
value: function has(char) {
return this._children.has(char);
}
/**
* @public
*
* @desc Get child node with specific character value
*
* @param {character} char - character to get
* @returns {TrieNode} node with character value
*/
}, {
key: "get",
value: function get$$1(char) {
return this._children.get(char);
}
/**
* @public
*
* @desc Set child node with specific character value
*
* @param {character} char - character to set
* @param {TrieNode} node - node to assign to character
*/
}, {
key: "set",
value: function set$$1(char, node) {
this._children.set(char, node);
}
/**
* @public
*
* @desc Delete child node with specific character value
*
* @param {character} char - character to delete
*/
}, {
key: "delete",
value: function _delete(char) {
this._children.delete(char);
}
}, {
key: "count",
get: function get$$1() {
return this._children.size;
}
/**
* @public
*
* @desc Get character value of node
*
* @returns {character} character
*/
// INFO: char or value?
}, {
key: "char",
get: function get$$1() {
return this._char;
}
/**
* @public
*
* @desc Checks if node is a complete word
*
* @returns {boolean} is complete word
*/
}, {
key: "isCompleteWord",
get: function get$$1() {
return this._isCompleteWord;
}
/**
* @public
*
* @desc Checks if node is a leaf node
*
* @returns {boolean} is leaf node
*/
}, {
key: "isLeafNode",
get: function get$$1() {
return this.count === 0;
}
}]);
return TrieNode;
}();
var PrimitiveType = {
BOOLEAN: "boolean",
FUNCTION: "function",
NUMBER: "number",
OBJECT: "object",
STRING: "string",
SYMBOL: "symbol",
UNDEFINED: "undefined"
};
/**
*
* @module super/trie
*/
/**
*
* Trie with superpowers! 💪
*
* @public
*
*/
var Trie = function () {
/** @private */
/**
* @public
*
* @desc Construct a Trie
*
* @param {Array<string>} iterable
*/
function Trie() {
var iterable = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
classCallCheck(this, Trie);
this._root = new TrieNode();
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = iterable[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var word = _step.value;
this.insert(word);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
/**
* @public
*
* @desc Get the root of the trie
*
* @returns {TrieNode} root node
*/
createClass(Trie, [{
key: "insert",
/**
* @public
*
* @desc Insert a string into the trie
*
* @param {string} word - string to insert
*/
value: function insert(word) {
if ((typeof word === "undefined" ? "undefined" : _typeof(word)) !== PrimitiveType.STRING) {
throw new Error("Unable to insert non-string value: " + word);
}
var curr = this.root;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = word[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var char = _step2.value;
if (curr.has(char)) {
// $FlowFixMe
curr = curr.get(char);
} else {
var node = new TrieNode(char);
curr.set(char, node);
curr = node;
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
curr._isCompleteWord = true;
}
/**
* @public
*
* @desc Remove a string from the trie
*
* @param {string} word - string to remove
*/
}, {
key: "remove",
value: function remove(word) {
if ((typeof word === "undefined" ? "undefined" : _typeof(word)) !== PrimitiveType.STRING) {
throw new Error("Unable to remove non-string value: " + word);
}
/**
* @public
*
* @desc Remove helper
*
* @param {TrieNode} curr - trie node
* @param {number} level - level in trie (0 -> height)
* @return {boolean} true if node is a leaf node and should be deleted; otherwise false
*/
// $FlowFixMe
function _remove(curr) {
var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
if (!curr) return false;
if (level === word.length) {
curr._isCompleteWord = false;
return curr.isLeafNode;
}
var char = word[level];
if (_remove(curr.get(char), level + 1)) {
curr.delete(char);
return curr.isLeafNode;
}
}
return _remove(this.root);
}
/**
* @public
*
* @desc Search for a node in the trie matching the query
*
* @param {string} query - string query to search for
* @returns {Match} match object
*/
}, {
key: "search",
value: function search(query) {
var node = this.root;
var index = 0;
while (index < query.length) {
node = node.get(query[index]);
if (!node) break;
index++;
}
return {
query: query,
matchedChars: index,
isMatch: query.length === index,
// $FlowFixMe
isCompleteWord: query.length === index && node.isCompleteWord,
node: node
};
}
/**
* @public
*
* @desc Check if the trie includes a word
*
* @param {string} word - full word to search for
* @returns {boolean} contains the word
*/
}, {
key: "includes",
value: function includes(word) {
var _search = this.search(word),
isCompleteWord = _search.isCompleteWord;
return isCompleteWord;
}
/**
* @public
*
* @alias includes(word)
*
* @desc Check if the trie contains a word
*
* @param {string} word - full word to search for
* @returns {boolean} contains the word
*/
}, {
key: "contains",
value: function contains(word) {
return this.includes(word);
}
/**
* @public
*
* @desc Check if the trie contains a prefix
*
* @param {string} prefix - prefix (i.e. partial word) to search for
* @returns {boolean} contains the prefix
*/
}, {
key: "startsWith",
value: function startsWith(prefix) {
var _search2 = this.search(prefix),
isMatch = _search2.isMatch;
return isMatch;
}
}, {
key: "root",
get: function get$$1() {
return this._root;
}
}]);
return Trie;
}();
exports.Trie = Trie;
Object.defineProperty(exports, '__esModule', { value: true });
})));