Reason #15 • January 15th, 2026

Enumerable#tally

On the fourth day of Enumerable week, let's have a look at Enumerable#tally.

tally counts the occurrences of each element in an enumerable and returns a hash where the keys are the elements and the values are their respective counts.

Ruby
fruits = ["🍎", "🍌", "🍎", "🍑", "🍌", "🍎"]
fruit_counts = fruits.tally
# => {"🍎"=>3, "🍌"=>2, "🍑"=>1}
      
JavaScript
const fruits = ["🍎", "🍌", "🍎", "🍑", "🍌", "🍎"];
const fruitCounts = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});
// => { 🍎: 3, 🍌: 2, 🍑: 1 }
      

You can also provide an existing hash to tally to accumulate counts into it. This can be useful for processing large datasets in batches or tallying collections from different places. Let's tally a log of fruits in real time, because why not?

Ruby
require "open3"

File.write("fruits.log", "")

# Append a fruit to fruits.log every 0.01 seconds
Thread.new do
  fruits = ["🍎", "🍌", "🍑"]
  File.open("fruits.log", "a") do |file|
    file.sync = true
    loop do
      file.puts(fruits.sample)
      sleep 0.01
    end
  end
end

# Tail and tally every 10 fruits
Open3.popen2("tail -f fruits.log") do |stdin, stdout, process|
  tally = Hash.new
  stdout.each_slice(10) do |lines|
    puts lines.map(&:chomp).tally(tally)
  end
end
      
JavaScript
const fs = require("node:fs");
const { spawn } = require("node:child_process");
const readline = require("node:readline");

fs.writeFileSync("fruits.log", "");

// Append a fruit to fruits.log every 0.01 seconds
const fruits = ["🍎", "🍌", "🍑"];
const writer = fs.createWriteStream("fruits.log", { flags: "a" });
setInterval(() => {
  const fruit = fruits[Math.floor(Math.random() * fruits.length)];
  writer.write(fruit + "\n");
}, 10);

// Tail and tally each fruit, log on every 10 fruits
const tail = spawn(
  "tail",
  ["-f", "fruits.log"],
  { stdio: ["ignore", "pipe", "inherit"] }
);

const lineReader = readline.createInterface({
  input: tail.stdout,
  crlfDelay: Infinity
});

const tally = {};
let n = 0;

(async () => {
  for await (const line of lineReader) {
    tally[line] = (tally[line] || 0) + 1;
    if (++n % 10 === 0) console.log(tally);
  }
})();
      

I'll let you be the judge of which implementation is more readable!

The tally method is a relatively recent addition to Enumerable, added in Ruby 2.7.0, released in December 2019. The ability to pass a hash to accumulate counts was added later in Ruby 3.1.

Reason #16 ?