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}
``````

## 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) }
``````