Search code examples
rubyanagram

Ruby - Anagram Codes


We have an array of words here:

words =  ['demo', 'none', 'tied', 'evil', 'dome', 'mode', 'live',
          'fowl', 'veil', 'wolf', 'diet', 'vile', 'edit', 'tide',
          'flow', 'neon']

My teacher wrote a program that prints out groups of words that are anagrams. Anagrams are words that have the same exact letters in them but in a different order. The output should look something like this:

["demo", "dome", "mode"]
["neon", "none"]
(etc)

And here's the solution my teacher showed us:

result = {}

words.each do |word|
  key = word.split('').sort.join
  if result.has_key?(key)
    result[key].push(word)
  else
    result[key] = [word]
  end
end

result.each do |k, v|
  puts "------"
  p v
end

I am bit confused how this program works such as when this part was set up result[key].push(word) and the part where it says result[key] = [word] I know this might be an off question a bit but can anyone one there can explain the solution line by line in a layman's term or in a way a dummy like me would understand.

PS. Sorry newbie here.


Solution

  • See the explanation inline comment:

    words.each do |word| #=>  iterate each word in words array. "word" variable will have value at a particular iteration
      key = word
      .split('') #=> splits word, if word is 'demo' on iteration then it will be: `['d', 'e', 'm', 'o']`
      .sort #=> sorts the splitted array, i.e. the array above will be: `['e', 'd', 'm', 'o']`
      .join #=> joins the array sorted in above operation, it will be: 'edmo'. Since this is last operation, it will be returned and saved in `key` variable
      if result.has_key?(key) #=> Check whether result(Hash) already has key: `edmo`, returns true if present
        result[key].push(word) #=> result['edmo'] will give an array value, which can push word in that array
      else #=> false when key is not present in result Hash.
        result[key] = [word] #=> then add key with an array such as: `result['edmo] = ['demo']`
      end
    end
    

    However, you can do the same in idiomatic way:

    result = Hash.new{|h, k| h[k] =[] } #=> if key does not exist then the default value will be an array.
    

    So, the above code will become:

    words.each do |word|
      key = word.split('').sort.join
      result[key] << word # no need to validate whether we have key in Hash or not
    end
    

    But, there's a problem with this approach of keeping value as an array. You will have duplicate data in your key if you have duplicate words in our words array. Problem can be solved by just changing array to set:

    require 'set'
    result = Hash.new{|h, k| h[k] = Set.new }
    

    Now, we're all good.