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

Anton Maminov @mamantoha

About: Ruby developer, Crystal enthusiast.

Location:
Ukraine
Joined:
Aug 31, 2017

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

Publish Date: Apr 11 '22
8 0

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

Comments 0 total

    Add comment