Ruby's tally
method was introduced in 2.7. It's a super convenient method on Enumerable
that I was recently able to use. Tally
counts each element's occurrences and returns a hash where the key is the element, and the value is the count.
Pre-Tally
If you are like me, any time you needed to get back a count of the times an element showed up in a collection, you headed over to StackOverflow and looked up how to do it. You probably ended up implementing something that looked like one of the examples below:
list = ["a", "b", "b", "a", "c", "d", "d", "d", "e"]
list.group_by { |v| v }.map { |k, v| [k, v.size] }.to_h
#=> {"a"=>2, "b"=>2, "c"=>1, "d"=>3, "e"=>1}
list.each_with_object(Hash.new(0)) { |v, h| h[v] += 1 }
#=> {"a"=>2, "b"=>2, "c"=>1, "d"=>3, "e"=>1}
Maybe you needed to count how many times some value showed up in a collection of hashes.
hashes = [{name: "Meagan" }, { name: "Meagan" }, { name: "Lauren" }]
hashes.group_by(&:itself).map { |k, v| k.merge(count: v.length)}
#=> [{ :name => "Meagan", :count => 2}, { :name => "Lauren", :count => 1 }]
Using tally
Tally
makes that collection into hash transformation so much easier.
list = ["a", "b", "b", "a", "c", "d", "d", "d", "e"]
list.tally
#=> {"a"=>2, "b"=>2, "c"=>1, "d"=>3, "e"=>1}
Bam. You did it. Let's use tally
on our collection of hashes example.
hashes = [{name: "Meagan" }, { name: "Meagan" }, { name: "Lauren" }]
h.tally
#=> {{:name=>"Meagan"}=>2, {:name=>"Lauren"}=>1}
hashes.tally.map { |k, v| k.merge({count: v}) }
#=> [{:name=>"Meagan", :count=>2}, {:name=>"Lauren", :count=>1}]
It still requires a little bit of munging to get the same return we got in the pre-tally example.
Conclusion & Further Reading
I hope that you found this useful and can add tally
to your toolkit. Knowing tally
means one less Google search I'll need to do in the future when I've got to count occurrences of an element in an array.
Top comments (1)
This is exactly what I needed! lol thank you, I didn't know about the tally method.
And the discussion on Ruby lang is super interesting, even years later.