Ruby散列数组:更新深度散列的更好方法



我有一个由以下形式的散列组成的数组:

array = [
{"id": 1, "items": [{"item_code": 1, "qty": 2},{"item_code": 2, "qty": 2}]},
{"id": 2, "items": [{"item_code": 3, "qty": 2},{"item_code": 4, "qty": 2}]},
{"id": 1, "items": [{"item_code": 5, "qty": 2},{"item_code": 6, "qty": 2}]},
{"id": 2, "items": [{"item_code": 7, "qty": 2},{"item_code": 8, "qty": 2}]}
]

我们有什么方便的方法可以根据ids:合并项目吗

预期输出:

array = [
{"id": 1, "items": [
{"item_code": 1, "qty": 2},
{"item_code": 2, "qty": 2},
{"item_code": 5, "qty": 2},
{"item_code": 6, "qty": 2}
]
},
{"id": 2, "items": [
{"item_code": 3, "qty": 2},
{"item_code": 4, "qty": 2},
{"item_code": 7, "qty": 2},
{"item_code": 8, "qty": 2}
]
}
]

我尝试了group_by,它不满足输出格式。

使用group_byeach_with_object:

ary.group_by { |elem| elem[:id] }.
each_with_object([]) do |(id, grouped_ary), out|
out << { id: id, items: grouped_ary.map { |h| h[:items] }.reduce(:|) }
end
=> [{:id=>1, :items=>[{:item_code=>1, :qty=>2},
{:item_code=>2, :qty=>2},
{:item_code=>5, :qty=>2},
{:item_code=>6, :qty=>2}]},
{:id=>2, :items=>[{:item_code=>3, :qty=>2},
{:item_code=>4, :qty=>2}, 
{:item_code=>7, :qty=>2},
{:item_code=>8, :qty=>2}]}] 

不需要group_by。直接从零开始使用each_with_object

array.
each_with_object({}) do |hash, acc|
acc[hash[:id]] ?
acc[hash[:id]][:items] |= hash[:items] : 
acc[hash[:id]] = hash
end.values
#⇒ [{:id=>1, :items=>[{:item_code=>1, :qty=>2},
#                     {:item_code=>2, :qty=>2},
#                     {:item_code=>5, :qty=>2},
#                     {:item_code=>6, :qty=>2}]},
#   {:id=>2, :items=>[{:item_code=>3, :qty=>2},
#                     {:item_code=>4, :qty=>2},
#                     {:item_code=>7, :qty=>2},
#                     {:item_code=>8, :qty=>2}]}]

首先我映射id,然后对于每个id,我用两个键进行散列:id和每个条目项的平面映射:

result = array.group_by { |e| e[:id] }.map { |id, entries| {id: id, items: entries.flat_map { |entry| entry[:items] }} }

您可以组合group_bytransform_values!(对于后者,您需要Ruby 2.4.0及更高版本,否则您可以使用each_with_object,正如@Jagdeep所指出的(

array.group_by { |item| item[:id] }
.transform_values! { |v| v.flat_map { |subitem| subitem[:items] } }
.map { |(id, items)| Hash["id", id, "items", items] }
array.each_with_object({}) { |g,h| h.update(g[:id]=>g[:items]) { |_,o,n| o+n } }.
map { |k,v| { id: k, items: v } }
#=> [{:id=>1, :items=>[{:item_code=>1, :qty=>2}, {:item_code=>2, :qty=>2},
#                      {:item_code=>5, :qty=>2}, {:item_code=>6, :qty=>2}]},
#    {:id=>2, :items=>[{:item_code=>3, :qty=>2}, {:item_code=>4, :qty=>2},
#                      {:item_code=>7, :qty=>2}, {:item_code=>8, :qty=>2}]}]

第一步是:

array.each_with_object({}) { |g,h| h.update(g[:id]=>g[:items]) { |_,o,n| o+n } }
#=> {1=>[{:item_code=>1, :qty=>2}, {:item_code=>2, :qty=>2},
#        {:item_code=>5, :qty=>2}, {:item_code=>6, :qty=>2}],
#    2=>[{:item_code=>3, :qty=>2}, {:item_code=>4, :qty=>2},
#        {:item_code=>7, :qty=>2}, {:item_code=>8, :qty=>2}]}

这利用了哈希#更新(也称为merge!(的形式,该形式使用块来确定在被合并的两个哈希中存在的密钥的值。有关三个块变量的定义,请参阅文档。

或者,可以写以下内容。

array.each_with_object({}) { |g,h| h.update(g[:id]=>g) { |_,o,n|
o.merge(n) { |k,oo,nn| k == :items ? oo+nn : oo } } }.values

最新更新