遍历Ruby Hash数组并计数否,的时代价值出现了?

sr4lhrrt  于 12个月前  发布在  Ruby
关注(0)|答案(4)|浏览(119)

我有一个Ruby哈希数组,

[{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}].

我想要一个最终的哈希值,它将是:

{:apple => 3,:banana=> 2,:pineapple=>1}

如何做到这一点?

nwwlzxa7

nwwlzxa71#

计算这样的直方图的常用技巧是使用Enumerable#group_by,然后Enumerable#map将结果Array s转换为size s,最后转换为Hash

ary = [{ fruit: 'apple' }, { fruit: 'apple' }, { fruit: 'apple' }, 
  { fruit: 'banana' }, { fruit: 'banana' }, { fruit: 'pineapple' }]

ary.
  group_by {|h| h.values.first }.
  map {|fruit, ary| [fruit.to_sym, ary.size]}.
  to_h
# => { apple: 3, banana: 2, pineapple: 1 }

但有一个更好的方法:一个名为MultiSet的数据结构完全可以满足您的需求。不幸的是,在Ruby核心库或stdlib中没有一个,但是你可以找到一些实现:

Multiset[*ary.map {|el| el.values.first.to_sym}]
# => #<Multiset:#3 :apple, #2 :banana, #1 :pineapple>

然而,几乎总是当你有一个数据结构,比如一个字符串的散列数组或类似的东西,那里有一个对象想要出来。毕竟,Ruby是一种面向对象的语言,而不是一种面向符号到字符串的哈希数组的语言。

class Fruit
  attr_reader :name

  def ==(other)
    name == other.name
  end

  def eql?(other)
    name.eql?(other.name)
  end

  def hash
    name.hash
  end

  def to_s
    name
  end

  def inspect
    "#<Fruit: #{name}>"
  end

  private

  attr_writer :name

  def initialize(name)
    self.name = name
  end
end

Multiset[Fruit.new('apple'), Fruit.new('apple'), Fruit.new('apple'), 
  Fruit.new('banana'), Fruit.new('banana'), Fruit.new('pineapple')]
# => #<Multiset:#3 #<Fruit: apple>, #2 #<Fruit: banana>, #1 #<Fruit: pineapple>>
iklwldmw

iklwldmw2#

@Simone说得对,你可以使用inject方法来计算出现次数,像这样:

[23] pry(main)> arr
=> [{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}]
[24] pry(main)> arr.inject({}) { |sum, val| sum[val[:fruit]] = sum[val[:fruit]].to_i + 1; sum }
=> {"apple"=>3, "banana"=>2, "pineapple"=>1}

这在代码中并不是很清楚,但是你需要在哈希上调用.to_i的原因是因为如果它返回nil(即第一次遇到“apple”),你不能做nil + 1,所以nil.to_i变成了零。我不认为这是超级清晰的代码以上,所以我想我会提到它。
虽然我的是一个“解决方案”,但Jorg在最佳实践方面是正确的,这将是一个研究OO解决方案的好机会。

balp4ylt

balp4ylt3#

创建一个具有默认值的哈希,并遍历数组中的每个哈希:

fruits = [{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}]
h = Hash.new(0)
fruits.each { |item| h[item[:fruit]] += 1 }
nbnkbykc

nbnkbykc4#

输入= [{:fruit=>“apple”},{:fruit=>“apple”},{:fruit=>“apple”},{:fruit=>“banana”},{:fruit=>“banana”},{:fruit=>“pineapple”}]

output = {}
input.each do |item|
  output[item[:fruit]] = 0 unless output[item[:fruit]]
  output[item[:fruit]] += 1
end
p output

相关问题