Ruby ハッシュの配列を反復処理し、その数を数えます。の価値が現れましたか?
概要
構造を持つハッシュの Ruby 配列があります
[{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}].
次のような最終ハッシュが必要です。
{:apple => 3,:banana=> 2,:pineapple=>1}
これを実現するにはどうすればよいでしょうか?
解決策
このようなヒストグラムを計算するための通常のトリックは、Enumerable#group_by を使用し、次に結果の配列をそのサイズに Enumerable#map して、最後にハッシュに変換することです。
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>>