如何在散列数组中找到由多个键分组的最大值?

时间:2012-05-16 15:25:55

标签: ruby arrays hash max uniq

拥有具有此类结构的数据。将按'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' => '?' } ]

要求'c'的最大值数组按“a”和“b”的每个唯一组合分组。

[ { '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'降序排序),uniq'a'和'b',以及反向数组。但我依赖于uniq_by的实现总是返回找到的第一个唯一项目。规范没有说明,所以我担心依赖于这种行为,因为它可能会在未来的版本中发生变化。还想知道这是否是一种非常低效的方法。

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

有更好,更有效的方法吗?如果你有一个更好的方法,你也可以解释一下,而不是只给我一个我可能无法解读的超级讨厌的单行。

1 个答案:

答案 0 :(得分:10)

实际上相当容易:

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,其"a""b"的组合(用Hash#values_at提取)作为键,所有以该组合作为值的哈希值。然后,我们映射此哈希,忽略键并从Enumerable#max_by数组中选择具有"c"最大值的元素。