Class: SkillExtractor::KeywordMatcher

Inherits:
Object
  • Object
show all
Defined in:
lib/skill_extractor/matcher.rb

Constant Summary collapse

KEYWORD =
:__keyword__
WORD_CHARS =
("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["_"]
WORD_SET =
WORD_CHARS.to_h { |c| [c, true] }.freeze

Instance Method Summary collapse

Constructor Details

#initialize(keywords = []) ⇒ KeywordMatcher

Returns a new instance of KeywordMatcher.



11
12
13
14
# File 'lib/skill_extractor/matcher.rb', line 11

def initialize(keywords = [])
  @trie = {}
  keywords.each { |kw| add(kw) }
end

Instance Method Details

#add(keyword) ⇒ Object



16
17
18
19
20
21
22
23
# File 'lib/skill_extractor/matcher.rb', line 16

def add(keyword)
  node = @trie
  kw = keyword.downcase
  kw.each_char do |ch|
    node = (node[ch] ||= {})
  end
  node[KEYWORD] = kw
end

#extract(sentence) ⇒ Object

Returns [[keyword, start, end], ...] spans, matching FlashText exactly.



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
# File 'lib/skill_extractor/matcher.rb', line 26

def extract(sentence)
  out = []
  return out if sentence.nil? || sentence.empty?

  sentence = sentence.downcase
  chars = sentence.chars
  current = @trie
  seq_start = 0
  seq_end = 0
  reset = false
  idx = 0
  n = chars.length
  while idx < n
    ch = chars[idx]
    if !WORD_SET[ch]
      if current.key?(KEYWORD) || current.key?(ch)
        longest = nil
        longer_found = false
        if current.key?(KEYWORD)
          longest = current[KEYWORD]
          seq_end = idx
        end
        if current.key?(ch)
          cont = current[ch]
          idy = idx + 1
          broke = false
          while idy < n
            inner = chars[idy]
            if !WORD_SET[inner] && cont.key?(KEYWORD)
              longest = cont[KEYWORD]
              seq_end = idy
              longer_found = true
            end
            if cont.key?(inner)
              cont = cont[inner]
            else
              broke = true
              break
            end
            idy += 1
          end
          if !broke && cont.key?(KEYWORD)
            longest = cont[KEYWORD]
            seq_end = idy
            longer_found = true
          end
          idx = seq_end if longer_found
        end
        current = @trie
        out << [longest, seq_start, idx] if longest
        reset = true
      else
        current = @trie
        reset = true
      end
    elsif current.key?(ch)
      current = current[ch]
    else
      current = @trie
      reset = true
      idy = idx + 1
      while idy < n
        break unless WORD_SET[chars[idy]]
        idy += 1
      end
      idx = idy
    end
    if idx + 1 >= n && current.key?(KEYWORD)
      out << [current[KEYWORD], seq_start, n]
    end
    idx += 1
    if reset
      reset = false
      seq_start = idx
    end
  end
  out
end