Skip to content

Commit 636dcb8

Browse files
authoredMay 30, 2018
Trie (#8)
* Added trie * Added hasWord * Added find
1 parent ccd42e6 commit 636dcb8

File tree

5 files changed

+222
-19
lines changed

5 files changed

+222
-19
lines changed
 

‎package-lock.json

+23-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"main": "lib/index.js",
66
"scripts": {
77
"lint": "eslint src/*.js test/*.js",
8+
"fix-lint": "eslint src/*.js test/*.js --fix",
89
"prepare": "./node_modules/babel-cli/bin/babel.js src --out-dir lib",
910
"precommit": "npm run lint",
1011
"test": "nyc --reporter=html --reporter=text mocha --require babel-core/register",
@@ -43,6 +44,7 @@
4344
"eslint-plugin-standard": "^3.0.1",
4445
"husky": "^0.14.3",
4546
"mocha": "^5.1.1",
46-
"nyc": "^11.7.1"
47+
"nyc": "^11.7.1",
48+
"random-words": "^1.1.0"
4749
}
4850
}

‎src/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import CircularList from './circular-linked-list.js';
66
import Stack from './stack.js';
77
import Queue from './queue.js';
88
import BST from './binary-search-tree.js';
9+
import Trie from './trie.js';
910

1011
export {
1112
List,
1213
DoublyList,
1314
CircularList,
1415
Stack,
1516
Queue,
16-
BST
17+
BST,
18+
Trie
1719
};

‎src/trie.js

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
'use strict';
2+
3+
class Node {
4+
constructor (value = null, isCompleteWord = false) {
5+
this.value = value;
6+
this.children = new Map();
7+
this.isCompleteWord = isCompleteWord;
8+
}
9+
10+
/**
11+
* @param {char} value
12+
* @param {Node} node
13+
*/
14+
setChild (value, node) {
15+
this.children.set(value, node);
16+
}
17+
18+
/**
19+
* @param {char} value
20+
* @return {Node}
21+
*/
22+
getChild (value) {
23+
return this.children.get(value);
24+
}
25+
}
26+
27+
export default class Trie {
28+
constructor () {
29+
this.root = new Node();
30+
}
31+
32+
/**
33+
* @param {string} word
34+
*/
35+
addWord (word) {
36+
if (!this.isValidWord(word)) {
37+
throw Error('Invalid Word');
38+
}
39+
40+
let current = this.root;
41+
42+
Array.from(word).forEach((letter) => {
43+
if (!current.getChild(letter)) {
44+
current.setChild(letter, new Node(letter));
45+
}
46+
47+
current = current.getChild(letter);
48+
});
49+
50+
current.isCompleteWord = true;
51+
}
52+
53+
/**
54+
* @return {Array<string>}
55+
*/
56+
getWords () {
57+
let words = [];
58+
59+
const getWords = (node, wordFragment) => {
60+
node.children.forEach((child) => {
61+
if (child.isCompleteWord) {
62+
words.push(wordFragment + child.value);
63+
}
64+
65+
getWords(child, wordFragment + child.value);
66+
});
67+
};
68+
69+
getWords(this.root, '');
70+
71+
return words;
72+
}
73+
74+
/**
75+
* @param {string} word
76+
* @return {Boolean}
77+
*/
78+
hasWord (word) {
79+
let current = this.root;
80+
81+
Array.from(word).forEach((letter) => {
82+
if (!current.getChild(letter)) {
83+
return false;
84+
}
85+
86+
current = current.getChild(letter);
87+
});
88+
89+
return current.isCompleteWord;
90+
}
91+
92+
/**
93+
* @param {string} prefix
94+
* @return {Node}
95+
*/
96+
find (prefix) {
97+
if (!this.isValidWord(prefix)) {
98+
throw Error('Invalid Word');
99+
}
100+
101+
let current = this.root;
102+
103+
Array.from(prefix).forEach((letter) => {
104+
if (!current.getChild(letter)) {
105+
return null;
106+
}
107+
108+
current = current.getChild(letter);
109+
});
110+
111+
return current.value === null ? null : current;
112+
}
113+
114+
/**
115+
* @param {string} word
116+
* @return {Boolean}
117+
*/
118+
isValidWord (word) {
119+
return (typeof (word) === 'string' || word instanceof String) && word.match(/^[A-z]+$/);
120+
}
121+
}

‎test/trie-test.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
'use strict';
2+
3+
import {Trie} from '../src/index.js';
4+
5+
const randomWords = require('random-words');
6+
const expect = require('chai').expect;
7+
8+
/**
9+
* @param {Array<string>} words
10+
* @return {Trie}
11+
*/
12+
function createTrie (words) {
13+
let trie = new Trie();
14+
15+
words.forEach((word) => {
16+
trie.addWord(word);
17+
});
18+
19+
return trie;
20+
}
21+
22+
describe('testing trie', function () {
23+
it('test adding values', function () {
24+
let words = randomWords(50);
25+
let trie = createTrie(words);
26+
27+
expect(trie.getWords()).to.include.members(words);
28+
});
29+
30+
it('test adding invalid values', function () {
31+
let trie = new Trie();
32+
33+
let invalidInputs = ['', 123, '$%^&', '123'];
34+
35+
invalidInputs.forEach((input) => {
36+
expect(trie.addWord.bind(trie, input)).to.throw(Error, 'Invalid Word');
37+
});
38+
});
39+
40+
it('test checking if word exists', function () {
41+
let words = randomWords(50);
42+
let trie = createTrie(words);
43+
44+
words.forEach((word) => {
45+
expect(trie.hasWord(word)).to.be.true;
46+
});
47+
});
48+
49+
it('test checking if word does not exist', function () {
50+
let words = ['call', 'dog', 'carded'];
51+
let trie = createTrie(words);
52+
53+
expect(trie.hasWord('cat')).to.be.false;
54+
expect(trie.hasWord('card')).to.be.false;
55+
});
56+
57+
it('test hasWord for empty trie', function () {
58+
let trie = new Trie();
59+
60+
expect(trie.hasWord('cat')).to.be.false;
61+
});
62+
63+
it('test finding node based on prefix', function () {
64+
let words = ['call', 'dog', 'carded'];
65+
let trie = createTrie(words);
66+
67+
expect(trie.find('ca').children.size).to.be.above(1);
68+
expect(trie.find('do').children.size).to.equal(1);
69+
expect(trie.find('o')).to.equal(null);
70+
expect(trie.find.bind(trie, '')).to.throw(Error, 'Invalid Word');
71+
});
72+
});

0 commit comments

Comments
 (0)
Please sign in to comment.