Reason #16 • January 16th, 2026

Enumerable#each_with_object

On the fifth day of Enumerable week, we're exploring Enumerable#each_with_object.

In my mind, each_with_object is inject (aka. reduce) but the one you actually want. Like inject, it takes an initial object and passes it along to each iteration. However, instead of having to explicitly return the object from the block, each_with_object automatically returns the object provided. It also gets the argument order right, that is, "each" element first, the "object" second - it's in the name!

Ruby
fruits = ["apple", "banana", "cherry"]
fruit_lengths = fruits.each_with_object({}) do |fruit, hash|
  hash[fruit] = fruit.length
end
# => {"apple"=>5, "banana"=>6, "cherry"=>6}
      
JavaScript
const fruits = ["apple", "banana", "cherry"];
const fruitLengths = fruits.reduce((obj, fruit) => {
  obj[fruit] = fruit.length;
  return obj;
}, {});
// => { apple: 5, banana: 6, cherry: 6 }
      

It tends to be the go-to method when aggregating data in ways that are not already covered by other methods like group_by and tally.

Ruby
views = [
  { timestamp: Time.parse("2026-01-01T00:00:00Z") },
  { timestamp: Time.parse("2026-01-01T01:00:00Z") },
  { timestamp: Time.parse("2026-01-01T01:10:00Z") },
  { timestamp: Time.parse("2026-01-01T02:15:00Z") },
  { timestamp: Time.parse("2026-01-01T02:55:00Z") },
]

views_by_hour = views.each_with_object(Hash.new(0)) do |view, hash|
  hash[view[:timestamp].strftime("%Y-%m-%d %H:00")] += 1
end
# => {
#   "2026-01-01 00:00" => 1,
#   "2026-01-01 01:00" => 2,
#   "2026-01-01 02:00" => 2
# }
      
JavaScript
const views = [
  { timestamp: new Date("2026-01-01T00:00:00Z") },
  { timestamp: new Date("2026-01-01T01:00:00Z") },
  { timestamp: new Date("2026-01-01T01:10:00Z") },
  { timestamp: new Date("2026-01-01T02:15:00Z") },
  { timestamp: new Date("2026-01-01T02:55:00Z") },
];

const viewsByHour = views.reduce((object, { timestamp }) => {
  const yyyy = timestamp.getUTCFullYear();
  const mm = String(timestamp.getUTCMonth() + 1).padStart(2, "0");
  const dd = String(timestamp.getUTCDate()).padStart(2, "0");
  const hh = String(timestamp.getUTCHours()).padStart(2, "0");
  const hour = `${yyyy}-${mm}-${dd} ${hh}:00`;

  object[hour] = (object[hour] || 0) + 1;
  return object;
}, {});
// => {
//   "2026-01-01 00:00": 1,
//   "2026-01-01 01:00": 2,
//   "2026-01-01 02:00": 2
// }
      

I guess we'll have to talk about default Hash values and Time#strftime some other day

each_with_object was added in Ruby 1.9, released on Christmas in 2007 to cover common use cases previously handled by inject, which was added in Ruby 1.8.

Reason #17 ?