141 lines
4.1 KiB
JavaScript
141 lines
4.1 KiB
JavaScript
function buildUsersTrie(users) {
|
|
const rootNode = new trie();
|
|
users.forEach(([id, name]) => {
|
|
rootNode.insert(name.toLowerCase(), [id, name]);
|
|
});
|
|
return JSON.stringify(rootNode.serialize());
|
|
}
|
|
class trie_node {
|
|
constructor() {
|
|
this.terminal = false;
|
|
this.children = new Map();
|
|
}
|
|
serialize() {
|
|
const { terminal, value, children } = this;
|
|
let mapped = {};
|
|
let numChildren = 0;
|
|
Object.keys(Object.fromEntries(children)).forEach((childKey) => {
|
|
numChildren += 1;
|
|
mapped[childKey] = children.get(childKey).serialize();
|
|
});
|
|
return {
|
|
t: this.terminal ? 1 : 0,
|
|
v: value,
|
|
c: numChildren > 0 ? mapped : undefined,
|
|
};
|
|
}
|
|
}
|
|
class trie {
|
|
constructor() {
|
|
this.root = new trie_node();
|
|
this.elements = 0;
|
|
}
|
|
serialize() {
|
|
return this.root.serialize();
|
|
}
|
|
get length() {
|
|
return this.elements;
|
|
}
|
|
get(key) {
|
|
const node = this.getNode(key);
|
|
if (node) {
|
|
return node.value;
|
|
}
|
|
return null;
|
|
}
|
|
contains(key) {
|
|
const node = this.getNode(key);
|
|
return !!node;
|
|
}
|
|
insert(key, value) {
|
|
let node = this.root;
|
|
let remaining = key;
|
|
while (remaining.length > 0) {
|
|
let child = null;
|
|
for (const childKey of node.children.keys()) {
|
|
const prefix = this.commonPrefix(remaining, childKey);
|
|
if (!prefix.length) {
|
|
continue;
|
|
}
|
|
if (prefix.length === childKey.length) {
|
|
// enter child node
|
|
child = node.children.get(childKey);
|
|
remaining = remaining.slice(childKey.length);
|
|
break;
|
|
}
|
|
else {
|
|
// split the child
|
|
child = new trie_node();
|
|
child.children.set(childKey.slice(prefix.length), node.children.get(childKey));
|
|
node.children.delete(childKey);
|
|
node.children.set(prefix, child);
|
|
remaining = remaining.slice(prefix.length);
|
|
break;
|
|
}
|
|
}
|
|
if (!child && remaining.length) {
|
|
child = new trie_node();
|
|
node.children.set(remaining, child);
|
|
remaining = "";
|
|
}
|
|
node = child;
|
|
}
|
|
if (!node.terminal) {
|
|
node.terminal = true;
|
|
this.elements += 1;
|
|
}
|
|
node.value = value;
|
|
}
|
|
remove(key) {
|
|
const node = this.getNode(key);
|
|
if (node) {
|
|
node.terminal = false;
|
|
this.elements -= 1;
|
|
}
|
|
}
|
|
map(prefix, func) {
|
|
const mapped = [];
|
|
const node = this.getNode(prefix);
|
|
const stack = [];
|
|
if (node) {
|
|
stack.push([prefix, node]);
|
|
}
|
|
while (stack.length) {
|
|
const [key, node] = stack.pop();
|
|
if (node.terminal) {
|
|
mapped.push(func(key, node.value));
|
|
}
|
|
for (const c of node.children.keys()) {
|
|
stack.push([key + c, node.children.get(c)]);
|
|
}
|
|
}
|
|
return mapped;
|
|
}
|
|
getNode(key) {
|
|
let node = this.root;
|
|
let remaining = key;
|
|
while (node && remaining.length > 0) {
|
|
let child = null;
|
|
for (let i = 1; i <= remaining.length; i += 1) {
|
|
child = node.children.get(remaining.slice(0, i));
|
|
if (child) {
|
|
remaining = remaining.slice(i);
|
|
break;
|
|
}
|
|
}
|
|
node = child;
|
|
}
|
|
return remaining.length === 0 && node && node.terminal ? node : null;
|
|
}
|
|
commonPrefix(a, b) {
|
|
const shortest = Math.min(a.length, b.length);
|
|
let i = 0;
|
|
for (; i < shortest; i += 1) {
|
|
if (a[i] !== b[i]) {
|
|
break;
|
|
}
|
|
}
|
|
return a.slice(0, i);
|
|
}
|
|
}
|