Ruby 基于一个哈希值在数组中组合哈希



我有一个哈希数组,看起来像:

[
  {"id"=>1, "name"=>"Batman", "net_worth"=>100, "vehicles"=>2},
  {"id"=>1, "name"=>"Batman", "net_worth"=>100, "vehicles"=>2},
  {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2},
  {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100, "vehicles"=>2}
]

我想在保留 id 值的同时组合基于哈希值,保留名称,并对net_worth和车辆值求和。

所以最终的数组看起来像:

[
  {"id"=>1, "name"=>"Batman", "net_worth"=>200, "vehicles"=>4},
  {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2},
  {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100, "vehicles"=>2}
]

这是您问题的解决方案。如您所见,您应该按 id 和名称对行进行分组,然后计算其他值的总和和和构建结果:

rows = [
    {"id"=>1, "name"=>"Batman", "net_worth"=>100, "vehicles"=>2},
    {"id"=>1, "name"=>"Batman", "net_worth"=>100, "vehicles"=>2},
    {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2},
    {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100, "vehicles"=>2}
]
groups = rows.group_by {|row| [row['id'], row['name']] }
result = groups.map do |key, values|
  id, name = *key
  total_net_worth = values.reduce(0) {|sum, value| sum + value['net_worth'] }
  total_vehicles = values.reduce(0) {|sum, value| sum + value['vehicles'] }
  { "id" => id, "name" => name, "net_worth" => total_net_worth, "vehicles" => total_vehicles }
end
p result

这里有两种方法可以处理任意数量的键值对,并且不依赖于键的名称(当然,"id""name"除外,它们是规范的一部分)。

使用update

这是一种使用 Hash#update(又名 merge!)形式的方法,它使用块来确定两个哈希中存在的键的值:

arr = [
  {"id"=>1, "name"=>"Batman",      "net_worth"=>100, "vehicles"=>2}, 
  {"id"=>1, "name"=>"Batman",      "net_worth"=>100, "vehicles"=>2}, 
  {"id"=>2, "name"=>"Superman",    "net_worth"=>100, "vehicles"=>2},
  {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100, "vehicles"=>2}
]   
arr.each_with_object({}) { |g,h|
  h.update(g["id"]=>g.dup) { |_,oh,nh|
    oh.update(nh) { |k,ov,nv|
      (['id','name'].include?(k)) ? ov : ov+nv } } }.values
  #=> [{"id"=>1, "name"=>"Batman", "net_worth"=>200, "vehicles"=>4}, 
  #    {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2},
  #    {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100,"vehicles"=>2}]   

使用group_by

这也可以通过使用 Enumerable#group_by 来完成,就像@maxd所做的那样,但下面是一个更紧凑和更通用的实现:

arr.map(&:dup).
    group_by { |row| row['id'] }.
    map { |_,arr|
      arr.reduce { |h, g|
        (g.keys - ['id','name']).each { |k| h[k] += g[k] }; h } }
  #=> [{"id"=>1, "name"=>"Batman", "net_worth"=>200, "vehicles"=>4}, 
  #    {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2},
  #    {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100,"vehicles"=>2}]   

arr.map(&:dup)是为了避免arr变异。我使用了没有参数的reduce,以避免需要复制具有键"id""name"键的键值对。

最新更新