计算一个元素在数组中出现的次数 Ruby

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

这是要处理的数组:

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

我希望结果是这样的:

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

我创建了一个名为 "count" 的新散列,然后我使用 .each 遍历数组然后 count[:count] += 1,但它没有给我我要找的东西。

可以通过这样的内置库调用来完成:

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

说明

.group_by(&:itself) 将采用所有相同的组件并将它们放在同一键下的散列中:

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

注意散列中的第一个条目如何包含两个相同元素的数组,而第二个条目如何包含一个元素的数组。要创建这些计数,我们可以在散列值中的数组上使用 .length 方法:

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

>> v.length
=> 2

然后我们可以使用 .map 将该代码应用于散列中的每个元素:

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

最后,.merge 将采用两个散列并将它们拼凑成一个散列:

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

主要有三种方法。

使用Enumerable#group_by

这就是@user12341234 在他或她的回答中所做的。

使用计数哈希

如果 arr 是你的数组,

arr.each_with_object(Hash.new(0)) { |g,h| h[g[:name]] += 1 }.
    map { |name, count| { :name=>name, :count=>count } }
  #=> [{:name=>"blake", :count=>2}, {:name=>"ashley", :count=>1}]

详情见Hash::new

使用 Hash#update(又名 merge!)的形式,它使用一个块来确定要合并的两个哈希中存在的键的值。

arr.each_with_object({}) { |g,h| h.update(g[:name]=>1) { |_,o,n| o+n } }.
    map { |name, count| { :name=>name, :count=>count } }
  #=> [{:name=>"blake", :count=>2}, {:name=>"ashley", :count=>1}]

有关详细信息,请参阅文档 Hash#update

请注意#2 和#3 的最后一行相同。

我不能说其中一种方法优于其他方法。

Ruby 2.7.0 引入的新 Enumerable#tally 到手了:

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

h.tally.map {|k, v| k.merge({count: v}) }
#=> [{:name=>"blake", :count=>2}, {:name=>"ashley", :count=>1}]