-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod.ts
126 lines (97 loc) · 2.45 KB
/
mod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
type Node<T> = {
edges: Record<string, Node<T>>;
value: T | null;
};
export class Radix<T = any> implements Node<T> {
edges = {};
value = null;
constructor(items: [string, T][] = []) {
for (const [text, value] of items) {
insert(text, value, this);
}
}
}
export function insert<T>(text: string, value: T, node: Node<T>): void {
let length = text.length;
while (length--) {
const prefix = text.substr(0, length + 1);
if (node.edges[prefix] /* traverse to the next node */) {
const rest = text.substr(length + 1);
if (rest === "" /* node already exists, update */) {
node.edges[prefix].value = value;
return;
}
return insert(rest, value, node.edges[prefix]);
}
}
let similar = false;
for (const sibling of Object.keys(node.edges)) {
const [common, x, y] = leftCollision(text, sibling);
if (common.length > 0) {
similar = true;
if (x === "" && y.length /* full match, not exhaustive */) {
const {
edges: {
[sibling]: { edges },
},
} = node;
// create two new nodes
node.edges[common] = {
edges: {
[y]: { edges, value: null },
},
value,
};
} else {
// create a new node using common prefix
node.edges[common] = {
edges: {},
value: null,
};
insert(x, value, node.edges[common]);
// move sibling into new node
const {
edges: {
[sibling]: { value: existingValue },
},
} = node;
insert(y, existingValue, node.edges[common]);
}
delete node.edges[sibling];
break;
}
}
if (!similar) {
node.edges[text] = {
edges: {},
value,
};
}
}
export function leftCollision(x: string, y: string): [string, string, string] {
let i = 0;
while (i < x.length && x[i] === y[i]) {
i++;
}
if (i > 0) {
return [x.substr(0, i), x.substr(i), y.substr(i)];
}
return ["", x, y];
}
export function find<T>(text: string, node: Node<T>): T | null {
const { length } = text;
for (let i = 0; i < length; i++) {
const prefix = text.substr(0, i + 1);
const {
edges: { [prefix]: next },
} = node;
if (next && i + 1 < length) {
return find(text.substr(i + 1), node.edges[prefix]);
}
if (next) {
const { value } = next;
return value;
}
}
return null;
}