繁体   English   中英

如何将哈希与哈希数组中的不同键/值对合并? Ruby

[英]How to merge hashes with different key/value pairs in array of hashes? Ruby

这是哈希数组:

array = [
  {:ID=>"aaa", :step2=>80},
  {:ID=>"aaa", :step1=>160},
  {:ID=>"aaa", :step3=>70},
  {:ID=>"bbb", :step1=>80}
]

我正在尝试合并具有相同:ID的散列并插入 value = 0 的缺失键,如下所示:

array = [
  {:ID=>"aaa", :step1 => 160, :step2 => 80, :step3 => 70},
  {:ID=>"bbb", :step1 => 80, :step2 => 0, :step3 => 0}
]

这是我的解决方案:

array = [
  {ID: "aaa", step2: 80},
  {ID: "aaa", step1: 160},
  {ID: "aaa", step3: 70},
  {ID: "bbb", step1: 80}
]

def group_by_id(hashes)
  # gather all IDs
  ids = hashes.map { |h| h[:ID] }.uniq
  keys = hashes.reduce([]) { |keys, hash| keys |= hash.keys }

  default_hash = {}
  keys.each do |key|
    default_hash[key] = 0
  end

  ids.map do |id|
    hashes.select { |hash| hash[:ID] == id }
          .reduce(default_hash) { |reduced, hash| reduced.merge(hash) }
  end
end

desired_array = [
  {ID: "aaa", step1: 160, step2: 80, step3: 70},
  {ID: "bbb", step1: 80, step2: 0, step3: 0}
]

output = group_by_id(array)
puts output
puts desired_array == output

#each_with_object方法在这里可能很有用。 在这种情况下,我们将传递一个 hash h ,它会针对array中的每个元素进行更新。 然后由#each_with_object方法返回 hash。

注意: ||=如果左侧为nilfalse ,则将右侧分配给左侧。

array.each_with_object({}) { |x, h| (h[x[:ID]] ||= {}).update(x) }

产量:

{"aaa"=>{"ID"=>"aaa", "step3"=>70, "step1"=>160, "step2"=>80}, 
 "bbb"=>{"ID"=>"bbb", "step1"=>80}}

然后我们只需要使用#values来获取我们想要的数据。

array
  .each_with_object({}) { |x, h| (h[x[:ID]] ||= {}).update(x) }
  .values

产量:

[{"ID"=>"aaa", "step3"=>70, "step1"=>160, "step2"=>80}, 
 {"ID"=>"bbb", "step1"=>80}]

但是您希望缺少用0填充的键。 为此,我们必须知道所有的键是什么,然后我们可以再次使用#each_with_object

grouped = array
           .each_with_object({}) { |x, h| (h[x[:ID]] ||= {}).update(x) }
           .values

all_keys = grouped.map(&:keys).flatten.uniq

grouped.map! { |h| all_keys.each_with_object(h) { |k, _h| _h[k] ||= 0 } }

现在grouped为:

[{"ID"=>"aaa", "step2"=>80, "step1"=>160, "step3"=>70}, 
 {"ID"=>"bbb", "step1"=>80, "step2"=>0, "step3"=>0}]

这可以分四个步骤完成。

array = [{:ID=>"aaa", :step2=>80}, {:ID=>"aaa", :step1=>160},
         {:ID=>"aaa", :step3=>70}, {:ID=>"bbb", :step1=>80}]

在添加缺少的零值键之前,构造一个 hash,其值是包含要返回的所需数组的哈希值

h = array.each_with_object({}) do |g,h|
  h.update(g[:ID]=>g) { |_,o,n| o.merge(n)}
end
  #=> {"aaa"=>{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70},
  #    "bbb"=>{:ID=>"bbb", :step1=>80, :step4=>40}}

请参阅Hash#update (又名merge )的形式,它采用一个块,该块返回正在合并的两个哈希中存在的键的值。 这里的块是:

{ |_,o,n| o.merge(n)}

块变量_保存公共键的值。 为该变量使用下划线的主要原因是向读者表明该键未在块计算中使用。 有关块变量on的定义,请参见文档。

构造出现在数组所有元素中的所有唯一stepX键的array

step_keys = array.flat_map { |g| g.keys }.uniq - [:ID]
  #=> [:step2, :step1, :step3, :step4]

请参阅Enumerable#flat_map

添加缺少的键

step_keys.each_with_object(h) { |k,g| g.each_value { |v| v[k] ||= 0 } }​
  #=> {"aaa"=>{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70, :step4=>0},
  #    "bbb"=>{:ID=>"bbb", :step1=>80, :step4=>40, :step2=>0, :step3=>0}}

现在

h #=> {"aaa"=>{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70, :step4=>0},
  #    "bbb"=>{:ID=>"bbb", :step1=>80, :step4=>40, :step2=>0, :step3=>0}}

返回一个包含h值的数组

h.values
  #=> [{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70, :step4=>0},
  #    {:ID=>"bbb", :step1=>80, :step4=>40, :step2=>0, :step3=>0}]

这四个语句可以组合成一个语句,但我不建议这样做,因为可读性会受到影响并且代码会更难测试。


根据要求,可以编写:

a = array.each_with_object({}) do |g,h|
  h.update(g[:ID]=>Hash.new(0).merge(g)) { |_,o,n| o.merge(n) }
end.values
  #=> [{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70},
  #    {:ID=>"bbb", :step1=>80, :step4=>40}]

这将返回与以前相同的数组,但现在:

a[0][:step4]
  #=> 0

即使 hash a[0]没有键:step4

请参阅Hash::new的形式,它接受一个参数但现在阻塞,参数是默认值 当定义了 hash

h = Hash.new(0)

然后(可能在将键添加到h之后),当h没有键k时, h[k]返回默认值

在确定此变体是否满足要求时,有明显的考虑因素需要权衡。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM