UNPKG

fast-trie-search

Version:

This package can be used to implement a Search-As-You-Type funtionality and uses a Trie data structure

95 lines (94 loc) 3.53 kB
import parseRegex from "regex-parser"; class TrieNode { m; //map w; //words constructor() { this.m = {}; this.w = []; } } const generateTrie = (objArray, searchProp, options = {}) => { let expandedObjArray = []; let trieKey = 1; //Default Options const defaults = { outputProps: Object.keys(objArray[0]), splitRegex: "/[ ]/", addKey: false, searchStartIndex: 0, excludeNodes: ["and", "the", "of", "with", "without", "in", "on", "&", "at", "as", "or", "type"] }; // Assign defualts const opts = Object.assign({}, defaults, options); objArray.forEach((element) => { /* Expand the searchProp out. If the search prop has a value "Stir-Fried Chicken with Jasmine rice", then create the following nodes: -> "StirFried Chicken with Jasmine rice" -> "Fried Chicken with Jasmine rice" -> "Chicken with Jasmine rice" -> "Jasmine rice" -> "rice" and typing any of these values should return "Stir-Fried Chicken with Jasmine rice" as one of the results Note that there is no node starting with "with" since its part of the "excludeNodes" option */ let expandedElement = element[searchProp].split(parseRegex(opts.splitRegex)); // For each of the expanded node, create the return object and push it into the expandedObjArray for (let i = 0; i < expandedElement.length; i++) { let nodeObj = {}; opts.outputProps.forEach((prop) => { nodeObj[prop] = element[prop]; }); if (opts.addKey === true) { nodeObj.k = trieKey++; } if (!opts.excludeNodes.includes(expandedElement[i].toLowerCase())) { expandedObjArray.push({ node: expandedElement.slice(i).join(" ").toLowerCase(), nodeObj }); } } }); console.log("total number of Trie words: ", expandedObjArray.length); const root = new TrieNode(); for (let i = 0; i < expandedObjArray.length; i++) { add(expandedObjArray[i], 0, root, opts.searchStartIndex); } // empty out the top level array as it serves no purpose and adds to the size root.w = []; return root; }; // searchStartIndex : the number of letters to type at which the search will return results. THis is // helpful in use cases where its unecessary to start at the first letter and helps reduce the trie size const add = (str, startIndex, root, searchStartIndex) => { // console.log("STR: ", str) // console.log("str.nd: ", str.nd) // console.log("startIndex: ", startIndex) // console.log("-------------------------------------") let node = str.node; if (startIndex === node.length) { root["w"] = []; root.w.push(str); return; } if (!root.m[node[startIndex]]) { root.m[node[startIndex]] = new TrieNode(); } if (startIndex >= searchStartIndex) { if (!root.w) root["w"] = []; root.w.push(str); } else { delete root.w; } add(str, startIndex + 1, root.m[node[startIndex]], searchStartIndex); }; const search = (str, startIndex, root) => { str = str.toLowerCase(); if (startIndex === str.length && startIndex !== 0) { return root.w; } if (!root.m[str[startIndex]]) return []; return search(str, startIndex + 1, root.m[str[startIndex]]); }; export { generateTrie, search, TrieNode };