【问题标题】:ruby array of hash merge array values based on key基于键的哈希合并数组值的红宝石数组
【发布时间】:2015-07-18 20:11:31
【问题描述】:

我对@9​​87654323@ 中的hash 中的array 有一些疑问。

例子:

a = [{236=>1}, {236=>1}, {237=>1}]

我想得到这样的结果:

a = [{236=>2}, {237=>1}]

如何根据ruby 中的key 合并值的array

【问题讨论】:

  • 发布后,您不应更改问题。它使答案和 cmets 毫无意义。您可以添加到您的问题中,在这种情况下,将其标识为编辑通常是个好主意。你会在我的回答中看到我这样做了。

标签: arrays ruby hash merge


【解决方案1】:

有几种方法可以做到这一点。这是一个使用方法Hash#update(又名merge!)的形式,它使用一个块来确定被合并的两个散列中存在的键的值:

a.each_with_object({}) { |g,h|
  h.update(g["order_detail_id"]=>g) { |_,oh,ng|
    { "order_detail_id" =>g["order_detail_id"],
      "quantity"=>oh["quantity"]+ng["quantity"] } } }.values
  #=> [{"order_detail_id"=>236, "quantity"=>2}, 
  #    {"order_detail_id"=>237, "quantity"=>1}] 

另一种方法使用Enumerable#group_by

a.group_by { |h| h["order_detail_id"] }.map { |id, a|
  { "order_detail_id"=>id, "quantity"=>a.reduce(0) { |t,g|
    t + g["quantity"] } } }
  #=> [{"order_detail_id"=>236, "quantity"=>2}, 
  #    {"order_detail_id"=>237, "quantity"=>1}] 

编辑:monoy 询问如何合并:

a = [{236=>1}, {236=>1}, {237=>1}]

当您拥有带有单个键值对的哈希时,通常最简单的方法是首先将它们转换为数组,以便轻松访问键和值:

b = a.map(&:to_a)
  #=> [[[236, 1]], [[236, 1]], [[237, 1]]] 

不幸的是,这有三层数组,而我们只需要两层。我们可以这样写:

b = a.map(&:to_a).flatten(1)
  #=> [[236, 1], [236, 1], [237, 1]] 

但更简单的方法是:

b = a.flat_map(&:to_a)
  #=> [[236, 1], [236, 1], [237, 1]] 

同样,我们可以用几种不同的方式合并数组。这是一个使用默认值为零的哈希:

b.each_with_object(Hash.new(0)) { |(k,v),h| h[k] += v }
  #=> {236=>2, 237=>1} 

我们有:

enum = b.each_with_object(Hash.new(0))
  #=> #<Enumerator: [[236, 1], [236, 1], [237, 1]]:each_with_object({})> 

我们可以把这个枚举器转换成一个数组,看看each会传入什么值:

enum.to_a
  #=> [[[236, 1], {}], [[236, 1], {}], [[237, 1], {}]]

我们可以使用Enumerator#next获取枚举器的每个值,并将块变量分配给该值。第一个传递给块的是:

(k,v),h = enum.next
  #=> [[236, 1], {}] 
k #=> 236 
v #=> 1 
h #=> {} 

所以在我们执行的块中:

h[k] += v

计算结果为:

h[236] += 1

意思是:

h[236] = h[236] + 1

哈希 h 目前为空,因此它没有密钥 236。因此,表达式右侧的h[236] 返回哈希的默认值零:

h[236] = 0 + 1 

现在:

h #=> {236=>1} 

现在将下一个元素传递到块中:

(k,v),h = enum.next
   #=> [[236, 1], {236=>1}] 
k #=> 236 
v #=> 1 
h #=> {236=>1} 

所以现在我们在块中执行表达式时不使用默认值:

h[k] += v
#=> h[k] = h[k] + v
#=> h[236] = h[236] + 1
#=> h[236] = 1 + 1

现在:

h #=> {236=>2}

现在将枚举器的第三个也是最后一个元素传递给块:

(k,v),h = enum.next
   #=> [[237, 1], {236=>2}] 
 k #=> 237 
 v #=> 1 
 h #=> {236=>2}

 h[k] += v
   #=> h[k] = h[k] + v
   #=> h[237] = h[237] + 1
   #=> h[236] = 0 + 1 # the default value of 0 applies

 h #=> {236=>2, 237=>1} 

我们已经完成了。

【讨论】:

  • 嗨@Cary Swoveland 如何像这样合并哈希数组:[{236=>1}, {236=>1}, {237=>1}]
【解决方案2】:
array = [{234 => 1}, {235 => 1}, {234 => 1}]

hashes = array.map(&:to_a).flatten
  .each_slice(2).with_object(Hash.new(0)) { |(k, v), h|
  h[k] += v
}.each_with_object([]) { |(k, v), a|
  a << { k => v }
}

【讨论】:

  • 无需删除,您只需编辑我的答案或评论修复即可。或者生病只写一个。
  • 太真实了。太真实了。不错的地方。
  • 对不起。是的。你说的对。 (犯各种错误:p)
  • 好的,刚刚测试。注意到 to_a 给出了一个 aarry 数组,当然,所以我决定展平并使用每个切片,但您也可以先映射或展平数组数组。或者您可以只调整块变量。无论如何,这似乎有效。
【解决方案3】:

我觉得这种方式可读:

a = [{236=>1}, {236=>1}, {237=>1}]
merge_with_addition = -> x,y {x.merge(y){|_,old,new|old+new}}

p a.reduce(&merge_with_addition) #=> {236=>2, 237=>1}

【讨论】:

  • 输出是一个哈希,而不是一个哈希数组。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-10
  • 1970-01-01
  • 2017-06-15
  • 1970-01-01
  • 2019-04-23
  • 2020-01-08
  • 1970-01-01
相关资源
最近更新 更多