DEV Community

Anton Maminov
Anton Maminov

Posted on

Crystal 1.4 Enumerable#tally now accepts an optional hash to count occurrences

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}
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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) }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)