我有一个带结构的Hashes Ruby数组
[{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}].
我想要一个最终的哈希值:
{:apple => 3,:banana=> 2,:pineapple=>1}
如何做到这一点?
答案 0 :(得分:2)
创建一个具有默认值的哈希并迭代数组中的每个哈希:
fruits = [{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}]
h = Hash.new(0)
fruits.each { |item| h[item[:fruit]] += 1 }
答案 1 :(得分:2)
计算这样的直方图的常用技巧是使用Enumerable#group_by
然后Enumerable#map
生成的Array
到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>>
答案 2 :(得分:2)
@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解决方案的好机会。