4

このような構造を持つデータを持っています。「c」の昇順になります。

[ { 'a' => 1, 'b' => 1, 'c' =>  1, 'd' => '?' },
  { 'a' => 1, 'b' => 1, 'c' =>  2, 'd' => '?' },
  { 'a' => 1, 'b' => 1, 'c' =>  3, 'd' => '?' },
  { 'a' => 1, 'b' => 2, 'c' =>  4, 'd' => '?' },
  { 'a' => 1, 'b' => 2, 'c' =>  5, 'd' => '?' },
  { 'a' => 2, 'b' => 1, 'c' =>  6, 'd' => '?' },
  { 'a' => 2, 'b' => 1, 'c' =>  7, 'd' => '?' },
  { 'a' => 2, 'b' => 1, 'c' =>  8, 'd' => '?' },
  { 'a' => 2, 'b' => 2, 'c' =>  9, 'd' => '?' },
  { 'a' => 2, 'b' => 2, 'c' => 10, 'd' => '?' } ]

「a」と「b」の一意の組み合わせごとにグループ化された「c」の最大値の配列が必要です。

[ { 'a' => 1, 'b' => 1, 'c' =>  3, 'd' => '?' },
  { 'a' => 1, 'b' => 2, 'c' =>  5, 'd' => '?' },
  { 'a' => 2, 'b' => 1, 'c' =>  8, 'd' => '?' },
  { 'a' => 2, 'b' => 2, 'c' => 10, 'd' => '?' } ]

他のキーは保持する必要がありますが、変換には関係ありません。これまでのところ、配列を逆にして(つまり、「c」で降順)、「a」と「b」でuniqし、配列を逆にすることです。しかし、私は常に最初に見つかった一意のアイテムを返す uniq_by の実装に依存しています。仕様にはそうは書いていないので、将来のバージョンで変更される可能性があるため、その動作に依存することは心配です。また、これが本当に非効率的な方法であるかどうかも疑問です。

@data.reverse!.uniq!{|record| [record['a'],record['b']]}.reverse!

これを行うためのより良い、より効率的な方法はありますか? より良い方法がある場合は、私が解読できないかもしれない非常に厄介なワンライナーを私に与える代わりに、それを説明してもらえますか.

4

1 に答える 1

12

それは実際にはかなり簡単です:

a.group_by { |h| h.values_at("a", "b") }.map { |_, v| v.max_by { |h| h["c"] } } 

または、より良いフォーマットで:

a.group_by do |h|
  h.values_at("a", "b") 
end.map do |_, v| 
  v.max_by { |h| h["c"] }
end

説明: まず、Enumerable#group_byを使用して、 ( Hash#values_atで抽出された)Hashと の組み合わせを キーとしてを作成し、その組み合わせを値としてすべてのハッシュを作成します。次に、このハッシュをマッピングし、キーを無視して、Enumerable#max_byを使用して配列から最大値を持つ要素を選択します。"a""b""c"

于 2012-05-16T15:38:16.970 に答える