43

I have an array with some elements. How can I get the number of occurrences of each element in the array?

For example, given:

a = ['cat', 'dog', 'fish', 'fish']

The result should be:

a2 #=> {'cat' => 1, 'dog' => 1, 'fish' => 2}

How can I do that?

Jon Schneider
  • 25,758
  • 23
  • 142
  • 170
Peter Becker
  • 431
  • 1
  • 4
  • 4
  • possible duplicate of [how to group numbers into different buckets in ruby](http://stackoverflow.com/questions/4300163/how-to-group-numbers-into-different-buckets-in-ruby) – Andrew Grimm May 02 '11 at 23:52

12 Answers12

44

You can use Enumerable#group_by to do this:

res = Hash[a.group_by {|x| x}.map {|k,v| [k,v.count]}]
#=> {"cat"=>1, "dog"=>1, "fish"=>2}
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
RameshVel
  • 64,778
  • 30
  • 169
  • 213
27
a2 = a.reduce(Hash.new(0)) { |a, b| a[b] += 1; a }
#=> {"cat"=>1, "fish"=>2, "dog"=>1}
Stefan
  • 109,145
  • 14
  • 143
  • 218
wallerdev
  • 291
  • 2
  • 3
14

Ruby 2.7 has tally method for this.

tally → a_hash

Tallies the collection, i.e., counts the occurrences of each element. Returns a hash with the elements of the collection as keys and the corresponding counts as values.

['cat', 'dog', 'fish', 'fish'].tally  

=> {"cat"=>1, "dog"=>1, "fish"=>2}
Shan
  • 633
  • 4
  • 20
10
a2 = {}
a.uniq.each{|e| a2[e]= a.count(e)}
u2ix
  • 592
  • 1
  • 5
  • 12
  • 1
    `count` has to traverse the entire array. Doing this for every value is quite inefficient. – Stefan Jun 28 '17 at 06:10
8

In 1.9.2 you can do it like this, from my experience quite a lot of people find each_with_object more readable than reduce/inject (the ones who know about it at least):

a = ['cat','dog','fish','fish']
#=> ["cat", "dog", "fish", "fish"]

a2 = a.each_with_object(Hash.new(0)) { |animal, hash| hash[animal] += 1 }
#=> {"cat"=>1, "dog"=>1, "fish"=>2}
Stefan
  • 109,145
  • 14
  • 143
  • 218
Michael Kohl
  • 66,324
  • 14
  • 138
  • 158
4

Use the count method of Array to get the count.

a.count('cat')
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Harry Joy
  • 58,650
  • 30
  • 162
  • 207
  • 4
    This would be a good solution if he really only needs one count at any given time. However, it is inefficient to do a large list this way. – Mark Thomas May 02 '11 at 13:58
3
m = {}

a.each do |e|
  m[e] = 0 if m[e].nil?
  m[e] = m[e] + 1
end

puts m
thekindofme
  • 3,846
  • 26
  • 29
3
a.inject({}){|h, e| h[e] = h[e].to_i+1; h }
#=> {"cat"=>1, "fish"=>2, "dog"=>1}

or n2 solution

a.uniq.inject({}){|h, e| h[e] = a.count(e); h }
#=> {"cat"=>1, "fish"=>2, "dog"=>1}
fl00r
  • 82,987
  • 33
  • 217
  • 237
3
a = ['cat','dog','fish','fish']
a2 = Hash[a.uniq.map {|i| [i, a.count(i)]}]
detunized
  • 15,059
  • 3
  • 48
  • 64
3
['cat','dog','fish','fish'].group_by(&:itself).transform_values(&:count)
=> {
     "cat" => 1,
     "dog" => 1,
    "fish" => 2
}
Alexey Kharchenko
  • 1,032
  • 5
  • 10
1
a = ['cat','dog','fish','fish']
a2 = Hash.new(0)
a.each do |e|
    a2[e] += 1
end
a2
look
  • 667
  • 10
  • 17
0

ruby fu!

count = Hash[Hash[rows.group_by{|x| x}.map {|k,v| [k, v.count]}].sort_by{|k,v| v}.reverse]
boulder_ruby
  • 38,457
  • 9
  • 79
  • 100