Enumeration refers to traversing, searching, filtering, and querying objects.
In Crystal, we have classes like Array, Hash, Range, etc. which get their enumeration features by including the Enumerable module.
This Enumerable module provides various methods e.g., #select, #map, #reduce, and #uniq which we frequently use, and less known #tally about which this post is.
#tally counts the number of occurrences of an element in a collection and returns a hash containing the count for all elements.
['a', 'b', 'c', 'b'].tally # => {'a'=>1, 'b'=>2, 'c'=>1}
Example
Consider the example of a list of words, and you want to count the total number of each char.
This type of problem is a perfect candidate to leverage our #tally method to calculate the total quantity of chars.
Consider you have words as follows:
word_one = "crystal"
word_two = "ruby"
Before
Calculating the quantity of chars in each word:
tally_one = word_one.chars.tally
# => {'c' => 1, 'r' => 1, 'y' => 1, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1}
tally_two = word_two.chars.tally
# => {'r' => 1, 'u' => 1, 'b' => 1, 'y' => 1}
total_tally = tally_one.merge(tally_two) { |k, v1, v2| v1 + v2 }
# => {'c' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1, 'u' => 1, 'b' => 1}
We have merged tally_one and tally_two and summed hash values to get the combined count of the chars.
After
To calculate the total, Crystal 1.4 Enumerable#tally now accepts an optional hash to count occurrences.
In this case, we can store the running tally of the number of items and pass it to the #tally method.
word_one = "crystal"
word_two = "ruby"
total_tally = {} of Char => Int32
word_one.chars.tally(total_tally)
word_two.chars.tally(total_tally)
total_tally
=> {'c' => 1, 'r' => 2, 'y' => 2, 's' => 1, 't' => 1, 'a' => 1, 'l' => 1, 'u' => 1, 'b' => 1}
As you can see in the above example in Crystal 1.4, we have passed the total_tally hash as an argument to the #tally method that stores the count of chars.
Finally, we can simplify the code to one line:
words = ["crystal", "ruby"]
total_tally = words.reduce({} of Char => Int32) { |acc, word| word.chars.tally(acc) }
 

 
    
Top comments (0)