Ruby |配列内のハッシュを合体させる


配列内にあるハッシュを合体させ、かつ同じキーのバリューの値を合算させるということをやってみました。

Paizaをやっていたら今回のような状況になったので、備忘録として記事にしました。
もし参考になれば使ってみてください。

また、他にもいいやり方があるよー、ちょっと違いますよーって方がいればコメントしていただければと思います。

開発環境

Ruby 2.5.3

やりたかったこと

配列内にあるハッシュ達を合体させ、配列ではなく一つのハッシュにしたかった。
ただし、同じキーが重複している際は、バリューの数値を合算する
イメージ的には下のような感じです。

hash = [{a: 1},{b: 2},{b: 3},{c: 4}]

#何か処理をした結果
#=> {:a=>1, :b=>5, :c=>4}

きっとレベルの高い人は今回のような状況にならないのかもしれません。笑

完成形

ナンセンスなやり方かもしれませんが、一応求めていた結果になりました。

2つのハッシュをマージする - Ruby Tips!
上の記事を参考にして、injectメソッドと併用してみました。

hash = [{a: 1},{b: 2},{b: 3},{c: 4}]

merged_hash = hash.inject do |h1, h2|
  h1.merge(h2) do |key, oldval, newval|
    oldval + newval
  end
end

p merged_hash
#=> {:a=>1, :b=>5, :c=>4}

バリューを前後の2つの引数に分けて、取り出せるのは知りませんでした。

失敗例

自分でなんとかここまでは思いつき、うまくいくと思ったのですが、
一つのハッシュにはなるけど、合算ができず....

hash = [{a: 1},{b: 2},{b: 3},{c: 4}]

merged_hash = hash.inject do |h1, h2|
    h1.merge(h2) 
end

p merged_hash
#=> {:a=>1, :b=>3, :c=>4}
# :b=>5 になって欲しかった...

使用したメソッドの参考例

今回使用したメソッドの通常の使用例をまとめてみました。

mergeメソッド

Ruby 2.6.0 リファレンスマニュアル > Hash#merge

ハッシュ同士を合体してくれるメソッド
合体する際に同じキーがある場合は、バリューの数値を合算してくれる。

h1 = {a: 1, b: 2, c: 3, d: 4}
h2 = {e: 5, f: 6}
h3 = {a: 10, b: 20}

p h1.merge(h2)
# => {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}

p h1.merge(h3)
# => {a: 11, b: 22, c: 3, d: 4}
# 値が合算される

injectメソッド

Ruby 2.6.0 リファレンスマニュアル > Enumerable#inject

配列を繰り返し処理する際に、前の要素と今の要素を条件に沿って処理してくれる。
今回の例は、足し算にしているので合計を算出してくれます。

numbers = [1,2,3,4,5]

numbers.inject do |n, m|
    n + m
end

# or

numbers.inject(&:+)

# => 21

Ruby 2.6 以降の書き方

色々とコメントをいただき、正しい情報を追記させていただきました。

Class: Hash (Ruby 2.6.1)
Ruby 2.6からは以下のような書き方も可能になったみたいです。
injectを使わなくてもマージかつ合算できるのは便利。
逆に言えば、injectmergeの組み合わせが定番だったからこそ、こうなったのかな?と。

hash = [{a: 1}, {b: 2}, {b: 3}, {c: 4}]

{}.merge(*hash) { |_key, v1, v2| v1 + v2 }
#=> {:a=>1, :b=>5, :c=>4}

{}.merge(*hash)
#=> {:a=>1, :b=>3, :c=>4}

他にもコメント欄に別の書き方が記載されています。参考にしてください。

さいごに

そもそもブロック内にまた別のブロックを作ることって実際ありなのかな?って一瞬思いましたが、他に何も思いつきませんでした。
もし、他にやり方があればぜひ教えていただきたいです。

読んでいただきありがとうございました!