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)