How to group by count in array without using loop

Solution 1:

x = arr.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }

Solution 2:

Only available under ruby 1.9

Basically the same as Michael's answer, but a slightly shorter way:

x = arr.each_with_object(Hash.new(0)) {|e, h| h[e] += 1}

In similar situations,

  • When the starting element is a mutable object such as an Array, Hash, String, you can use each_with_object, as in the case above.
  • When the starting element is an immutable object such as Numeric, you have to use inject as below.

    sum = (1..10).inject(0) {|sum, n| sum + n} # => 55

Solution 3:

There is a short version which is in ruby 2.7 => Enumerable#tally.

[1,2,1,3,5,2,4].tally  #=> { 1=>2, 2=>2, 3=>1, 5=>1, 4=>1 }

# Other possible usage

(1..6).tally { |i| i%3 }   #=> { 0=>2, 1=>2, 2=>2 }

Solution 4:

Yet another - similar to others - approach:

result=Hash[arr.group_by{|x|x}.map{|k,v| [k,v.size]}]
  1. Group by each element's value.
  2. Map the grouping to an array of [value, counter] pairs.
  3. Turn the array of paris into key-values within a Hash, i.e. accessible via result[1]=2 ....

Solution 5:

arr.group_by(&:itself).transform_values(&:size)
#=> {1=>2, 2=>2, 3=>1, 5=>1, 4=>1}