Ruby哈希数组:更好地更新哈希的更好方法

时间:2018-07-27 12:37:53

标签: ruby-on-rails 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}]}
        ]

我们是否有任何方便的方法来根据ID合并项目?

预期输出:

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。

5 个答案:

答案 0 :(得分:2)

使用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}]}] 

答案 1 :(得分: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}]}]

答案 2 :(得分:1)

您可以结合使用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] }

答案 3 :(得分:1)

首先,我映射ID,然后为每个ID用两个键进行哈希处理:ID和每个条目项的展平图:

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

答案 4 :(得分:1)

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}]}

这利用Hash#update(也称为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