Ruby 如何通过组合具有不同值的键来合并具有相同ID的两个哈希

Ruby 如何通过组合具有不同值的键来合并具有相同ID的两个哈希,ruby,hash,Ruby,Hash,我有一个哈希数组,如下所示: hashes = [ {id: 1, value: 'something', source: 'a'}, {id: 1, value: 'something', source: 'b'}, {id: 2, value: 'something', source: 'a'}, {id: 3, value: 'something', source: 'c'} ] 我需要这个: new_hashes = [ {id: 1, value: 'someth

我有一个哈希数组,如下所示:

hashes = [
  {id: 1, value: 'something', source: 'a'},
  {id: 1, value: 'something', source: 'b'},
  {id: 2, value: 'something', source: 'a'},
  {id: 3, value: 'something', source: 'c'}
]
我需要这个:

new_hashes = [
  {id: 1, value: 'something', source: ['a', 'b']},
  {id: 2, value: 'something', source: ['a']},
  {id: 3, value: 'something', source: ['c']}
]
我该怎么做?我尝试了以下方法:

merge_array = -> x,y { x.merge(y){|key, old, new| Array(old).push(new)} }
hashes.reduce &merge_array
但这一切都融合在一起了。我不想合并具有不同ID的哈希。

哈希。
hashes.
    group_by { |e| [e[:id], e[:value]] }.
    map { |_, g|
      g.first.clone.
          tap { |t|
            t[:source] = g.reduce([]) { |a, e| a << e[:source] }
          }
    }
按{| e |[e[:id],e[:value]]对u进行分组。 映射{| g,g| g、 首先,克隆。 抽头| t[:source]=g.reduce([]){a,e | a@Amadan在他的回答中使用。
groupby
Enumerable#update
(aka
merge!
)是可互换的,因为当使用一个时,通常可以使用另一个。我将在这里展示如何使用
update

hashes.each_with_object({}) do |g,h|
  h.update(g[:id] => g.merge(source: [g[:source]])) do |_,oh,nh|
    oh.merge(source: oh[:source] + nh[:source])
  end
end.values
  #=> [{:id=>1, :value=>"something", :source=>["a", "b"]},
  #    {:id=>2, :value=>"something", :source=>["a"]},
  #    {:id=>3, :value=>"something", :source=>["c"]}] 
首先,请注意,
h.update(k=>v)
h.update({k=>v})
的缩写。它使用(aka
merge!
)的形式,使用一个块来确定合并的两个哈希中存在的键的值。该块有三个块变量,即公共键(
),更新的哈希值(
oh
,“o”表示“旧”,“h”表示该值,因为该值是哈希值)和合并的哈希值(
nh
,“n”表示“新”)

步骤如下

e = hashes.each_with_object({})
  #=> #<Enumerator: [
  #     {:id=>1, :value=>"something", :source=>"a"},
  #     {:id=>1, :value=>"something", :source=>"b"},
  #     {:id=>2, :value=>"something", :source=>"a"}, 
  #     {:id=>3, :value=>"something", :source=>"c"}
  #   ]:each_with_object({})> 
然后执行块计算

h.update(g[:id]  => g.merge(source: [g[:source]]))
  #=> h.update(1 => g.merge(source: ["a"]))
  #=> h.update(1 =>{:id=>1, :value=>"something", :source=>["b"]})
  #=> {1=>{:id=>1, :value=>"something", :source=>["b"]}}
g,h = e.next
  #=> [{:id=>1, :value=>"something", :source=>"b"},
  #    {1=>{:id=>1, :value=>"something", :source=>["a"]}}] 
g #=>  {:id=>1, :value=>"something", :source=>"b"} 
h #=>  {1=>{:id=>1, :value=>"something", :source=>["a"]}} 
在执行此合并之前,
h
为空,这意味着要合并的两个哈希没有公共键。
update
的值解析块因此未被调用

现在,
e
update
指示生成下一个值并将其传递给块。块变量被分配给该值并执行块计算

h.update(g[:id]  => g.merge(source: [g[:source]]))
  #=> h.update(1 => g.merge(source: ["a"]))
  #=> h.update(1 =>{:id=>1, :value=>"something", :source=>["b"]})
  #=> {1=>{:id=>1, :value=>"something", :source=>["b"]}}
g,h = e.next
  #=> [{:id=>1, :value=>"something", :source=>"b"},
  #    {1=>{:id=>1, :value=>"something", :source=>["a"]}}] 
g #=>  {:id=>1, :value=>"something", :source=>"b"} 
h #=>  {1=>{:id=>1, :value=>"something", :source=>["a"]}} 
请注意,
h
已更新。现在计算:

h.update(g[:id] => g.merge(source: [g[:source]])) do |_,oh,nh|
  oh.merge(source: oh[:source] + nh[:source])
end
  #=> {1=>{:id=>1, :value=>"something", :source=>["a", "b"]}} 
作为

上述表达式简化为

h.update(1 => {:id=>1, :value=>"something", :source=>["b"]}) do |_,oh,nh|
  oh.merge(source: oh[:source] + nh[:source])
end
由于合并的两个散列都有一个公共键
1
,因此调用该块以确定合并散列中
1
的值:

_ = 1
oh = h[1]
  #=> {:id=>1, :value=>"something", :source=>["a"]}
nh = g.merge(source: [g[:source]]) 
  #=> g.merge(source: ["b"])
  #=> {:id=>1, :value=>"something", :source=>["b"]}
我使用了下划线(一个有效的局部变量名)来表示公共键,以向读卡器发出在块计算中未使用该键的信号

oh.merge(source: oh[:source] + nh[:source])
  #=> oh.merge(source: ["a", "b"])  
  #=> {:id=>1, :value=>"something", :source=>["a", "b"]}
对于
e
生成的其余元素,计算结果类似。因此,我们得到:

f = hashes.each_with_object({}) do |g,h|
  h.update(g[:id] => g.merge(source: [g[:source]])) do |_,oh,nh|
    oh.merge(source: oh[:source] + nh[:source])
  end
end
  #=> {1=>{:id=>1, :value=>"something", :source=>["a", "b"]},
  #    2=>{:id=>2, :value=>"something", :source=>["a"]},
  #    3=>{:id=>3, :value=>"something", :source=>["c"]}} 

最后一步是返回
f.values

您尝试编写了哪些代码?请显示您的尝试它与Rails无关。
map
的块的inard也可以编写
g.first.merge(源代码:g.reduce([]){a,e | a
f = hashes.each_with_object({}) do |g,h|
  h.update(g[:id] => g.merge(source: [g[:source]])) do |_,oh,nh|
    oh.merge(source: oh[:source] + nh[:source])
  end
end
  #=> {1=>{:id=>1, :value=>"something", :source=>["a", "b"]},
  #    2=>{:id=>2, :value=>"something", :source=>["a"]},
  #    3=>{:id=>3, :value=>"something", :source=>["c"]}}