aho-corasick.ts 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. const WORDEND = Symbol('wordEnd');
  2. const FAIL = Symbol('fail');
  3. type Node = Map<string, Node> & {
  4. [WORDEND]: boolean,
  5. [FAIL]: Node | undefined
  6. };
  7. function createNode(): Node {
  8. const node = new Map<string, Node | undefined>() as Node;
  9. node[WORDEND] = false;
  10. node[FAIL] = undefined;
  11. return node;
  12. }
  13. function createKeywordFilter(keys: string[] | Set<string>) {
  14. const root = createNode();
  15. // Create a trie with extra fields and information
  16. const put = (key: string) => {
  17. const len = key.length;
  18. let node = root;
  19. for (let idx = 0; idx < len; idx++) {
  20. const char = key[idx];
  21. if (node.has(char)) {
  22. node = node.get(char)!;
  23. } else {
  24. const newNode = createNode();
  25. node.set(char, newNode);
  26. node = newNode;
  27. }
  28. }
  29. // If a new node is created, mark it as a word end when loop finish
  30. if (node !== root) {
  31. node[WORDEND] = true;
  32. }
  33. };
  34. keys.forEach(put);
  35. // const build = () => {
  36. const queue: Node[] = [root];
  37. while (queue.length) {
  38. const beginNode = queue.pop()!;
  39. beginNode.forEach((node, char) => {
  40. let failNode = beginNode[FAIL];
  41. while (failNode && !failNode.has(char)) {
  42. failNode = failNode[FAIL];
  43. }
  44. node[FAIL] = failNode ? failNode.get(char) : root;
  45. queue.push(node);
  46. });
  47. }
  48. // };
  49. // build();
  50. return (text: string) => {
  51. let node: Node | undefined = root;
  52. for (let i = 0, textLen = text.length; i < textLen; i++) {
  53. const char = text[i];
  54. while (node && !node.has(char)) {
  55. node = node[FAIL];
  56. }
  57. node = node ? node.get(char)! : root;
  58. if (node[WORDEND]) {
  59. return true;
  60. }
  61. }
  62. return false;
  63. };
  64. }
  65. export default createKeywordFilter;