Enumerable#tally
Hash · Updated March 31, 2026 · Enumerable The tally method counts the occurrences of each element in a collection and returns the results as a hash. Each unique element becomes a key, and its count becomes the corresponding value. This is a straightforward way to analyze the distribution of values in your data.
Basic Usage
Count all elements in an array:
fruits = ["apple", "banana", "apple", "orange", "banana", "apple"]
fruits.tally
# => {"apple"=>3, "banana"=>2, "orange"=>1}
The method iterates through the collection once and builds the hash of counts.
With Numbers
dice_rolls = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
dice_rolls.tally
# => {3=>2, 1=>2, 4=>1, 5=>3, 9=>1, 2=>1, 6=>1}
With Ranges
Ranges are enumerables, so tally works on them directly:
(1..6).tally
# => {1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>1}
# Count even vs odd in a range
(1..10).tally { |n| n.even? ? "even" : "odd" }
# => {"odd"=>5, "even"=>5}
The block form transforms each element before counting, giving you flexibility in how you categorize.
Custom Counting with Hash Argument
You can provide an existing hash to tally to customize how counts are accumulated:
votes = ["yes", "no", "yes", "yes", "abstain", "no", "yes"]
# Pre-initialize all expected keys
tally = votes.tally({ "yes" => 0, "no" => 0, "abstain" => 0, "absent" => 0 })
# => {"yes"=>3, "no"=>2, "abstain"=>1, "absent"=>0}
This is useful when you need all possible categories represented in the result, even if some have zero counts.
Use Case: Survey with All Options
survey_responses = ["satisfied", "neutral", "satisfied", "dissatisfied", "satisfied"]
# Ensure all rating levels appear in results
all_options = ["very_dissatisfied", "dissatisfied", "neutral", "satisfied", "very_satisfied"]
tally = survey_responses.tally(Hash.new(0).merge!(all_options.map { |k| [k, 0] }.to_h))
# Or more simply, start with the hash you need:
tally = survey_responses.tally(all_options.product([0]).to_h)
# => {"satisfied"=>3, "neutral"=>1, "dissatisfied"=>1}
Comparison to group_by + count
Before tally, you would often combine group_by with count to achieve similar results:
words = ["cat", "dog", "fish", "cat", "dog", "cat"]
# Old approach with group_by + count
words.group_by(&:itself).transform_values(&:count)
# => {"cat"=>3, "dog"=>2, "fish"=>1}
# Modern approach with tally
words.tally
# => {"cat"=>3, "dog"=>2, "fish"=>1}
The tally method is more concise and performs the same operation in a single pass, making it both cleaner and slightly more efficient.
When group_by Still Shines
group_by remains useful when you need the actual grouped elements rather than just counts:
words = ["cat", "dog", "fish", "cat", "dog", "cat"]
# Get the actual elements grouped
words.group_by(&:itself)
# => {"cat"=>["cat", "cat", "cat"], "dog"=>["dog", "dog"], "fish"=>["fish"]}
# tally only gives you counts, not the elements
words.tally
# => {"cat"=>3, "dog"=>2, "fish"=>1}
With Custom Enumerables
tally works with any object that includes the Enumerable module:
# Via to_enum on a Hash
stock = { apples: 50, bananas: 30, oranges: 25, apples: 20 }
stock.to_enum.tally
# Note: Hash keys are unique, so this counts key occurrences
# Fibonacci as an Enumerable
fib = Enumerator.new do |y|
a, b = 0, 1
loop do
y << a
a, b = b, a + b
end
end
fib.take(10).tally
# => {0=>1, 1=>3, 2=>1, 3=>1, 4=>1, 5=>1, 6=>1, 8=>1}
Return Value
tally always returns a hash. The keys are the unique elements from the collection, and the values are integers representing how many times each element appeared.
[].tally
# => {}
[1, 1, 1].tally
# => {1=>3}
Practical Examples
Analyzing Log Files
log_levels = ["INFO", "DEBUG", "INFO", "ERROR", "INFO", "DEBUG", "ERROR", "INFO"]
log_levels.tally
# => {"INFO"=>4, "DEBUG"=>2, "ERROR"=>2}
Word Frequency Analysis
sentence = "the quick brown fox jumps over the lazy dog the fox"
sentence.split.tally
# => {"the"=>2, "quick"=>1, "brown"=>1, "fox"=>2, "jumps"=>1, "over"=>1, "lazy"=>1, "dog"=>1}
Color Distribution in Inventory
inventory = [:red, :blue, :red, :green, :blue, :red, :blue, :blue]
inventory.tally
# => {:red=>3, :blue=>4, :green=>1}
See Also
Enumerable#group_by— groups elements into categoriesEnumerable#count— counts elements matching a conditionEnumerable#reduce— accumulates values into a single result