0

I have a Ruby array that looks something like this:

animals = %w(dog cat bird cat dog bird bird cat)

I need to get a count of each unique item in the array. I could do something like this:

dogs = 0
cats = 0
birds = 0

animals.each do |animal|
  dogs += 1 if animal == 'dog'
  cats += 1 if animal == 'cat'
  birds += 1 if animal == 'bird'
end

...but this approach is too verbose. What is the most concise way of calculating these unique counts in Ruby?

Andrew
  • 227,796
  • 193
  • 515
  • 708
  • Check the answer here http://stackoverflow.com/questions/5470725/how-to-group-by-count-in-array-without-using-loop – Alireza Oct 27 '14 at 19:04

4 Answers4

5

I guess what you're looking for is count:

animals = %w(dog cat bird cat dog bird bird cat)
dogs = animals.count('dog') #=> 2
cats = animals.count('cat') #=> 3
birds = animals.count('bird') #=> 3
Surya
  • 15,703
  • 3
  • 51
  • 74
2
animals.uniq.map { |a| puts a, animals.count(a)}
Surya
  • 15,703
  • 3
  • 51
  • 74
Bala
  • 11,068
  • 19
  • 67
  • 120
1

Another way of doing it using group_by

animals = %w(dog cat bird cat dog bird bird cat)
hash = animals.group_by {|i| i}
hash.update(hash) {|_, v| v.count}

#=> hash = {"dog"=>2, "cat"=>3, "bird"=>3}
rohit89
  • 5,745
  • 2
  • 25
  • 42
  • Your last line is very cool. I'm familiar with the form of `update` that takes a block, but I haven't seen it with a hash `merge!`d into itself. You might consider writing the block variables as `|_,v,_|`. – Cary Swoveland Oct 27 '14 at 20:46
0

The easiest way to do this would be to store the counts in a hash like so:

animals = %w(dog cat bird cat dog bird bird cat)
animal_counts = {}
animals.each do |animal|
  if animal_counts[animal]
    animal_counts[animal] += 1
  else
    animal_counts[animal] = 1
  end
end

This stores each unique item in the array as a key with they count as the value. It improves upon your code because it doesn't require advance knowledge of the contents of the array.

ptd
  • 3,024
  • 16
  • 29
  • Well, this seems like an overkill to me. You say you don't need an advance knowledge of the contents of the array, I agree, that's right. But, how will you access the count of `dog`, `cat`, or `bird` if you don't pass them as key in your newly created Hash? – Surya Oct 27 '14 at 19:05
  • You can simplify this to: `animals.each_with_object({}) {|a,c| c[a] = (c[a] ||= 0)+1} => {"dog"=>2, "cat"=>3, "bird"=>3}`. – Cary Swoveland Oct 27 '14 at 20:28