Search code examples
arraysrubyhashmapenumerable

Count how many times an element appears in an array in Ruby


This is the array that is going to be processed:

[{:name => "blake"}, {:name => "blake"}, {:name => "ashley"}]

and I would like the result to be like this:

[{:name => "blake", :count => 2}, {:name => "ashley", :count => 1}]

I created a new hash called "count" then i used .each to iterate through the array and then count[:count] += 1, but it's not giving me what I'm looking for.


Solution

  • It can be done with built-in library calls like this:

    a = [{:name => "blake"}, {:name => "blake"}, {:name => "ashley"}]
    a.group_by(&:itself)                   # group all of the identical elements together
     .map{|k, v| k.merge(count: v.length)} # add the "count" element to the hash
    

    Explanation

    .group_by(&:itself) will take all the identical components and put them in a hash together under the same key:

    >> h = a.group_by(&:itself)
    => {
         {:name=>"blake"}  => [{:name=>"blake"}, {:name=>"blake"}],
         {:name=>"ashley"} => [{:name=>"ashley"}]
        }
    

    Notice how the first entry in the hash has an array of two identical elements, and the second entry has an array of one element. To create counts of those, we can use the .length method on the arrays in the hash's values:

    >> k, v = h.first
    >> v
    => [{:name=>"blake"}, {:name=>"blake"}]
    
    >> v.length
    => 2
    

    Then we can use .map to apply that code to every element in the hash:

    >> h.map{|k, v| [k, v.length]}.to_h
    => {
         {:name=>"blake"}=>2,
         {:name=>"ashley"}=>1
       }
    

    Finally, .merge will take two hashes and piece them together into a single hash:

    >> {:name=>"blake"}.merge({:count => 1})
    => {:name=>"blake", :count=>1}