Ruby Tuesday – Enumerable#group_by

Today’s Ruby Tuesday is on Enumerable#group_by.

Enumerable#group_by takes a block and returns a hash whose keys are the distinct values returned from the block, and the values are a list of items for which the block returned the key they are associated with.

This sounds a lot more complex than if you see it in code, so we will do a group_by of the range of numbers from one to ten, and group them by the result of calling even? on them.

(1..10).group_by(&:even?)
# => {false=>[1, 3, 5, 7, 9], true=>[2, 4, 6, 8, 10]}

We can se we have a hash with the two keys false and true, and the value for the key of false is a list of all the odd numbers, and the value for true is the list of the even numbers.

As the keys are just the return value, we can pass an array of strings, and group them by their first letter.

["foo", "bar", "bazz"].group_by(&:chr)
# => {"f"=>["foo"], "b"=>["bar", "bazz"]}

Or we can group them by their length,

["foo", "bar", "bazz"].group_by(&:length)
# => {3=>["foo", "bar"], 4=>["bazz"]}

Or, if we want to find some anagrams, we can group the words by the result of sorting their characters.

["tar", "rat", "bar", "rob", "art", "orb"].group_by {|word| word.chars.sort}
# => {["a", "r", "t"]=>["tar", "rat", "art"],
#  ["a", "b", "r"]=>["bar"],
#  ["b", "o", "r"]=>["rob", "orb"]}

This is also useful for when you are trying to get the start of some data to plot in a graph or a histogram, such as the length example above, or if we wanted to get a hash to be able to plot the number of words in the system dictionary against their length.

File.readlines("/usr/share/dict/words").
     map(&:strip).
     group_by(&:length).
     reduce({}) {|accum, kv| word_length, words = kv; accum[word_length] = words.length; accum}
# => {1=>52,
#  2=>160,
#  3=>1420,
#  5=>10230,
#  4=>5272,
#  8=>29989,
#  7=>23869,
#  9=>32403,
#  6=>17706,
#  11=>26013,
#  10=>30878,
#  12=>20462,
#  14=>9765,
#  16=>3377,
#  15=>5925,
#  20=>198,
#  19=>428,
#  17=>1813,
#  13=>14939,
#  18=>842,
#  21=>82,
#  22=>41,
#  23=>17,
#  24=>5}

–Proctor