forked from Choices-js/Choices
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkmp.ts
More file actions
87 lines (73 loc) · 2.12 KB
/
kmp.ts
File metadata and controls
87 lines (73 loc) · 2.12 KB
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
import { Options } from '../interfaces';
import { Searcher, SearchResult } from '../interfaces/search';
function kmpSearch(pattern: string, text: string): number {
if (pattern.length === 0) {
return 0; // Immediate match
}
// Compute longest suffix-prefix table
const lsp = [0]; // Base case
for (let i = 1; i < pattern.length; i++) {
let j = lsp[i - 1]; // Start by assuming we're extending the previous LSP
while (j > 0 && pattern.charAt(i) !== pattern.charAt(j)) {
j = lsp[j - 1];
}
if (pattern.charAt(i) === pattern.charAt(j)) {
j++;
}
lsp.push(j);
}
// Walk through text string
let j = 0; // Number of chars matched in pattern
for (let i = 0; i < text.length; i++) {
while (j > 0 && text.charAt(i) !== pattern.charAt(j)) {
j = lsp[j - 1]; // Fall back in the pattern
}
if (text.charAt(i) === pattern.charAt(j)) {
j++; // Next char matched, increment position
if (j === pattern.length) {
return i - (j - 1);
}
}
}
return -1; // Not found
}
export class SearchByKMP<T extends object> implements Searcher<T> {
_fields: string[];
_haystack: T[] = [];
constructor(config: Options) {
this._fields = config.searchFields;
}
index(data: T[]): void {
this._haystack = data;
}
reset(): void {
this._haystack = [];
}
isEmptyIndex(): boolean {
return !this._haystack.length;
}
search(_needle: string): SearchResult<T>[] {
const fields = this._fields;
if (!fields || !fields.length || !_needle) {
return [];
}
const needle = _needle.toLowerCase();
const results: SearchResult<T>[] = [];
let count = 0;
for (let i = 0, j = this._haystack.length; i < j; i++) {
const obj = this._haystack[i];
for (let k = 0, l = this._fields.length; k < l; k++) {
const field = this._fields[k];
if (field in obj && kmpSearch(needle, (obj[field] as string).toLowerCase()) !== -1) {
results.push({
item: obj[field],
score: count,
rank: count + 1,
});
count++;
}
}
}
return results;
}
}