【问题标题】:Merge and consolidate an array of hashes合并和合并散列数组
【发布时间】:2014-11-22 03:17:38
【问题描述】:

我正在尝试获取这个哈希值数组...

items => [{:name=>"item a", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
 {:name=>"item b", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
 {:name=>"item a", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
 {:name=>"item b", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
 {:name=>"item c", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
 {:name=>"item d", :count=>1, :contributors=>["51db6d58bd02861e96000004"]}]

并获得如下所示的输出...

items => [{:name=>"item a", :count=>2, :contributors=>["51db6d58bd02861e96000004", "51db6d58bd02861e96000004"]},
     {:name=>"item b", :count=>2, :contributors=>["51db6d58bd02861e96000004, 51db6d58bd02861e96000004"]},
     {:name=>"item c", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
     {:name=>"item d", :count=>1, :contributors=>["51db6d58bd02861e96000004"]}]

我想按名称分组并合并计数和贡献者,如上图所示。我怎么做? group_by,减少?有人可以发一个例子吗?

【问题讨论】:

  • 请展示您到目前为止尝试过的内容。
  • 什么是items =>?那部分使您的表达无效。
  • 你的意思是items = [ ... ] 吗?
  • 您选择了@falsetru 的答案(不错的选择),但没有支持他的答案。只是想让你知道你可以同时做这两件事,以防你不知道。

标签: ruby arrays hash enumerable


【解决方案1】:

我最初发布了另一个解决方案,却发现 @falsetru 已经发布了几乎相同的答案,所以它又回到了绘图板上。不完全是,实际上,因为对于这种类型的问题,有两种常见的攻击线,所以我选择了另一种。

def combine(items)
  items.each_with_object({}) do |g,h|
    h.update({ g[:name] => g }) do |k,ov,nv|
      { name: g[:name], count: (ov[:count] + nv[:count]),
        contributors: (ov[:contributors] + nv[:contributors]) }
    end
  end.values
end

这使用Hash#update(又名merge!)的形式,它采用一个块来确定被合并的两个散列共享的每个键的值。

注意,我允许:count 具有除1:contributors 以外的值,以具有包含多个值的值(数组)。

combine(items)
 #=>[{:name=>"item a", :count=>2,
 #    :contributors=>["51db6d58bd02861e96000004", "51db6d58bd02861e96000004"]},
 #   {:name=>"item b", :count=>2,
 #    :contributors=>["51db6d58bd02861e96000004", "51db6d58bd02861e96000004"]},
 #   {:name=>"item c", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
 #   {:name=>"item d", :count=>1, :contributors=>["51db6d58bd02861e96000004"]}]

【讨论】:

  • 感谢 Cary,这似乎也是一个很好的方法,并且得到了很好的解释。
【解决方案2】:

使用Enumerable#group_by

items = [
  {:name=>"item a", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
  {:name=>"item b", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
  {:name=>"item a", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
  {:name=>"item b", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
  {:name=>"item c", :count=>1, :contributors=>["51db6d58bd02861e96000004"]},
  {:name=>"item d", :count=>1, :contributors=>["51db6d58bd02861e96000004"]}
]
items.group_by { |h| h[:name] }.map { |key, hs|
  {:name => key,
   :count => hs.inject(0) { |c, h| c + h[:count] },
   :contributors => hs.map { |h| h[:contributors] }
  }
}

# => [{:name=>"item a", :count=>2,
#      :contributors=>[["51db6d58bd02861e96000004"], ["51db6d58bd02861e96000004"]]},
#     {:name=>"item b", :count=>2,
#      :contributors=>[["51db6d58bd02861e96000004"], ["51db6d58bd02861e96000004"]]},
#     {:name=>"item c", :count=>1, :contributors=>[["51db6d58bd02861e96000004"]]},
#     {:name=>"item d", :count=>1, :contributors=>[["51db6d58bd02861e96000004"]]}]

【讨论】:

  • 考虑到我最初发布了相同的解决方案(然后看到您的解决方案已经启动,将其删除),+1 是显而易见的。
  • 这非常有效,因为这是第一个答案,所以我接受了它。感谢您的帮助。
  • 恭喜你达到 10 万!
猜你喜欢
  • 2018-07-02
  • 2015-02-03
  • 2011-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
相关资源
最近更新 更多