class WordyNumber::WordyMatch

Constants

DEFAULT_DICT_FACTS
DEFAULT_DICT_FILE_NAME
DEFAULT_DICT_FILE_PATH
DEFAULT_SEPARATOR
PUBLIC_DIR

Dictioary hash structures looks like following dict_hash = { 1 => [{:word_form=>“a”, :numeric_form=>“2”}], ,…, 23 => [{:word_form=>“disestablismentarianism”, :numeric_form=>“34737822547636827426476”},

{:word_form=>"electroencephalographic", :numeric_form=>"35328763623742564727442"}],

}

Attributes

dict[RW]
num[RW]
num_filtered_patterns[RW]
num_patterns[RW]

Public Class Methods

concat_array_of_lists_of_strings(array_of_lists, separator=DEFAULT_SEPARATOR) click to toggle source
# File lib/wordy_number/wordy_match.rb, line 103
def self.concat_array_of_lists_of_strings(array_of_lists, separator=DEFAULT_SEPARATOR)
  # remove empty elements
  array_of_lists.each do |list|
    list.delete_if{ |elem| elem.length == 0 }
  end
  # remove empty lists
  array_of_lists.select!{ |list| list.size > 0 }

  # calculate product
  result = if array_of_lists.size > 1
    array_of_lists[0].product(*array_of_lists[1..-1]).map{ |elem| elem.join(separator) }
  elsif array_of_lists.size == 1
    array_of_lists[0]
  else
    # array_of_lists.size == 0
    []
  end

  result
end
join_numbers_together_in_str(num_str, separator=DEFAULT_SEPARATOR) click to toggle source
# File lib/wordy_number/wordy_match.rb, line 124
def self.join_numbers_together_in_str(num_str, separator=DEFAULT_SEPARATOR)
  res_str = num_str.gsub(/(\d)#{DEFAULT_SEPARATOR}+(\d)/, '\1\2')
  if res_str != num_str
    return join_numbers_together_in_str(res_str)
  else
    return res_str
  end
end
new(user_dictionary=DEFAULT_DICT_FILE_PATH) click to toggle source

dictionary is expected to have one word per line

# File lib/wordy_number/wordy_match.rb, line 27
def initialize(user_dictionary=DEFAULT_DICT_FILE_PATH)
  @dict = user_dictionary
  scan_dict!(dict)
end

Public Instance Methods

dict_hash() click to toggle source
# File lib/wordy_number/wordy_match.rb, line 40
def dict_hash
  @dict_hash = {} unless @dict_hash

  @dict_hash
end
display_dict_stats() click to toggle source
# File lib/wordy_number/wordy_match.rb, line 46
def display_dict_stats
  dh = dict_hash
  dh.keys.each{ |key| puts "#{key} -> #{dh[key].size}" }
end
display_matches(args = {num_file: nil, num_list: nil}) click to toggle source

sub = WordyMatch.new sub.display_matches num_file: “my_nums.txt” sub.display_matches num_list: [2255, 225563]

# File lib/wordy_number/wordy_match.rb, line 54
def display_matches(args = {num_file: nil, num_list: nil})
  # get user inputs
  num_file = args[:num_file]
  num_list = args[:num_list]
  num_file_path = File.expand_path(num_file, PUBLIC_DIR) if num_file
  results = {}

  # form numbers array
  numbers = []
  if num_file_path
    File.open(num_file_path, "r") do |file|
      file.each_line do |line|
        numbers << line
      end
    end
  elsif num_list.class == Array
    numbers = num_list
  else
   # user hasn't provide num_file, so ask him/her explicitly
    puts "Comma-separated list of numbers: "
    numbers = gets.strip.split(",")
  end

  # get output
  numbers.each do |number|
    puts "---------------------------------------"
    puts "Matches for #{number.to_s}"
    matches = set_num_and_find_matches(number)
    results[number.to_s] = matches
    if matches.size > 0
      matches.each do |match|
        puts match
      end
    else
      puts "No matches"
    end
  end

  results
end
num=(user_number) click to toggle source
# File lib/wordy_number/wordy_match.rb, line 32
def num=(user_number)
  @num = user_number.to_s
  @num.strip!
  @num.gsub!(/\D/, "")

  @num
end
set_num_and_find_matches(user_number) click to toggle source
# File lib/wordy_number/wordy_match.rb, line 95
def set_num_and_find_matches(user_number)
  set_num(user_number)

  # find matches and filter them
  split_arnd_0_1_n_find_matches
  filtered_list
end

Private Instance Methods

filtered_list(list_of_num_strs=self.num_patterns.clone) click to toggle source

filter out patterns with consecutive digits

# File lib/wordy_number/wordy_match.rb, line 186
def filtered_list(list_of_num_strs=self.num_patterns.clone)
  unless list_of_num_strs
    raise InvalidInputError
  end
  list_of_num_strs.delete_if{ |pattern| pattern.split(DEFAULT_SEPARATOR).join("") =~ /\d{2}/ }
  self.num_filtered_patterns = list_of_num_strs

  self.num_filtered_patterns
end
find_all_matches(num_str=self.num, original_call=true, pattern_length=num_str.length) click to toggle source
# File lib/wordy_number/wordy_match.rb, line 142
def find_all_matches(num_str=self.num, original_call=true, pattern_length=num_str.length)
  patterns = []
  if num_str.size == 0  # first deal with the edge cases
    return patterns
  end
  if pattern_length == 1  # first deal with the edge cases
    if dict_hash[pattern_length]
      times = 0
      dict_hash[pattern_length].each do |dict_word|
        if num_str == dict_word
          times += 1
          patterns << dict_word.word_form
        end
      end
      if times == 0
        patterns << num_str
      end
    end
  elsif pattern_length > 0  # general case
    if dict_hash[pattern_length]
      dict_hash[pattern_length].each do |dict_word|
        matched_index = num_str =~ /#{dict_word.numeric_form}/
        if matched_index
          str1 = matched_index > 0 ? num_str[0..(matched_index - 1)] : ""
          str2 = dict_word.word_form  # it has word_form, but str1 and str3 still have numeric_form
          str3 = num_str[(matched_index + pattern_length)..-1]
          patterns += self.class.concat_array_of_lists_of_strings([find_all_matches(str1, false), [str2], find_all_matches(str3, false)])
        end
      end
    end
    patterns += find_all_matches(num_str, false, pattern_length-1)
  else
    raise NonReachableCodeError
  end
  patterns.uniq!

  if original_call
    self.num_patterns = patterns
  end

  return patterns
end
scan_dict!(dict_file_path) click to toggle source
# File lib/wordy_number/wordy_match.rb, line 221
def scan_dict!(dict_file_path)
  dict_file_path = dict_file_path || dict || DEFAULT_DICT_FILE_PATH

  # reset dict_hash
  dh = dict_hash
  dh.clear

  # load dictionary
  File.open(dict_file_path, "r") do |file|
    file.each_line do |line|
      dw = DictWord.new(line)
      dh[dw.word_form.length] = [] unless dh[dw.word_form.length]  # `dw.word` is sanitized but `line` is not
      dh[dw.word_form.length] << DictWord.new(line)
    end
  end

  dh
end
set_num(user_number) click to toggle source

set_num() is kind of alias to num=(), but helps in chaining

# File lib/wordy_number/wordy_match.rb, line 136
def set_num(user_number)
  self.num = user_number

  self
end
split_arnd_0_1_n_find_matches(num_str=self.num) click to toggle source

it enhances the performance of long numbers carrying 1s or 0s

# File lib/wordy_number/wordy_match.rb, line 197
def split_arnd_0_1_n_find_matches(num_str=self.num)
  # split given number_string at 0's and 1's
  splits = num_str.gsub(/[^01]/, "").split("")
  split_num_str = num_str.split(/[01]/)
  # obtain patterns for each sub_number_string
  split_results = split_num_str.map do |sub_num_str|
    find_all_matches(sub_num_str)
  end
  # obtain combined result
  combined_splits_and_split_results = []
  split_results.each_index do |index|
    combined_splits_and_split_results << split_results[index]
    combined_splits_and_split_results << [splits[index]] if index <= splits.length - 1
  end
  combined_splits_and_split_results.select!{ |x| x.length > 0 }
  results = self.class.concat_array_of_lists_of_strings(combined_splits_and_split_results)
  # join numbers togeter in each results element
  results.map!{ |str| self.class.join_numbers_together_in_str(str) }
  # set num_patterns
  self.num_patterns = results

  results
end