6

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.

OmG
  • 18,337
  • 10
  • 57
  • 90

3 Answers3

4

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.

Surya
  • 15,703
  • 3
  • 51
  • 74
2

It basically ensures that you are operating on valid array.

When you start with empty hash {}, the value of result[key] will be nil for any given key - as the key still does not exist in that hash.

01:  if result.has_key?(key)
02:    result[key].push(word)
03:  else
04:    result[key] = [word]
05:  end

So, line 01 checks whether a key is already present in result hash - if it is, then, one can access it using expression result[key], and assuming its an array, push another word into that array.

If key still does not exist in result hash, then, line 04 sets it up by assigning a value which is a single element array [word].


If you are more familiar with Ruby, then, one could write something like below and avoid whole if-else dance:

words.each do |word|
  key = word.split('').sort.join
  result[key] ||= []
  result[key] << word
end
Wand Maker
  • 18,476
  • 8
  • 53
  • 87
0

The program computes a key for each word by sorting its letters (key = word.split('').sort.join). If two or more words are anagrams of each other, they will have the same key.

For example, the key of the words neon and none is enno.

Then, the words are added in the result hash. This hash has as keys the anagram keys discussed above. The values of the hash are arrays of words (strings).

Here's the rest of the (important) code annotated:

# If there is an entry in the hash for the current anagram key 
if result.has_key?(key)
  # Then add the current word in the array, together with all the
  # other anagrams that are already there (because they share the
  # same key)
  result[key].push(word)
else
  # If the key has not yet been created, then create it and assign
  # as a value a new array that only contains the current word.
  result[key] = [word]
end
Cristian Lupascu
  • 39,078
  • 16
  • 100
  • 137