rubyでオブジェクトを綺麗にまとめたい

[code]

    [
      {
        :id => 1,
        :data => 1
      },
      {
        :id => 1,
        :data => 2
      },
      {
        :id => 2,
        :data => 1
      },
      {
        :id => 2,
        :data => 2
      }
    ]
    
[/code]

↓こうしたい

[code]

    [
      {
        :id => 1,
        :data => [1,2]
      },
      {
        :id => 2,
        :data => [1,2]
      },
    ]
    
[/code]

ループがたくさんになってしまう上に、うまくいきません。




こんな感じでどうでしょうか？

[code]

    objects = [
      {
        :id => 1,
        :data => 1
      },
      {
        :id => 1,
        :data => 2
      },
      {
        :id => 2,
        :data => 1
      },
      {
        :id => 2,
        :data => 2
      }
    ]
    objects
      .map {|h| [h[:id], h[:data]] }
      .group_by(&:first)
      .map {|id, arr| { id: id, data: arr.map(&:last) } }
    # => [{:id=>1, :data=>[1, 2]}, {:id=>2, :data=>[1, 2]}]
    
[/code]

多少読みづらくても構わないなら、こんな書き方もできます。  
ループの回数は少なくなりそうです。

[code]

    data_holder = Hash.new {|h, k| h[k] = { id: k, data: [] } }
    objects.each_with_object(data_holder) { |object, holder|
      holder[object[:id]][:data] << object[:data]
    }.values
    
[/code]

`each_with_object` の代わりに `tap` を使うパターンもあります。

[code]

    Hash.new {|h, k| h[k] = { id: k, data: [] } }.tap {|hash|
      objects.each do |obj|
        id, data = obj.values_at(:id, :data)
        hash[id][:data] << data
      end
    }.values
    
[/code]

お好きな方法で実装してみてください。

