首页 > 解决方案 > 合并 ruby​​ on rails 中相同键名的值

问题描述

我想合并同名值的 id

hashes = [{
    id: 3456824,
    name: 'John'
},{
    id: 6578954,
    name: 'Vicky'
},{
    id: 987456,
    name: 'John'
}]

预期的:

[{
    id: [3456824,987456],
    name: 'John'
},{
    id: 6578954,
    name: 'Vicky'
}]

我如何在 ruby​​ on rails 中实现这一点?

标签: ruby-on-railsruby

解决方案


这是计算所需结果的两种方法。

使用带块的Hash::new形式

hashes.each_with_object(Hash.new { |h,k| h[k] = [] }) do |g,h|
  h[g[:name]] << g[:id]
end.map { |name,id| { id: id, name: name } }
  #=> [{:id=>[3456824, 987456], :name=>"John"},
  #    {:id=>[6578954], :name=>"Vicky"}]

这个计算1的第一步是

hashes.each_with_object(Hash.new { |h,k| h[k] = [] }) do |g,h|
  h[g[:name]] << g[:id]
end
  #=> {"John"=>[3456824, 987456], "Vicky"=>[6578954]}

如果定义了哈希

h = Hash.new { |h,k| h[k] = [] }

并且(可能在添加键值对之后)h没有 key kh[k]

h[k] << v

导致块{ |h,k| h[k] = [] }被执行,导致键值对k=>[]被添加到h,然后<< v被执行,h[k][]变为[k]


请注意,这将返回问题所要求的:id=>[6578954],而不是。:id=>6578954将所有值都:id返回一个数组可以避免在:id处理此操作的返回值的后续代码中检查是否返回一个数组或整数。

如果:id=>6578954, 是需要的,可以写

hashes.each_with_object(Hash.new { |h,k| h[k] = [] }) do |g,h|
  h[g[:name]] << g[:id]
end.transform_values { |v| v.size==1 ? v.first : v }.
    map { |name,id| { id: id, name: name } }
  #=> [{:id=>[3456824, 987456], :name=>"John"},
  #    {:id=>6578954, :name=>"Vicky"}]

请参阅Hash#transform_values

使用Hash#update (aka merge!) 的形式,它使用一个块来确定合并的两个哈希中存在的键的值

arr.each_with_object({}) do |g,h|
  h.update(g[:name]=>[g[:id]]) { |_,o,n| o+n }
end.map { |name,id| { id: id, name: name } }
  #=> [{:id=>[3456824, 987456], :name=>"John"},
  #    {:id=>[6578954], :name=>"Vicky"}]

如果需要:id=>6578954,而不是:id=>[6578954],则:

arr.each_with_object({}) do |g,h|
  h.update(g[:name]=>g[:id]) { |_,o,n| [*o,n] }
end.map { |name,id| { id: id, name: name } }
  #=> [{:id=>[3456824, 987456], :name=>"John"},
  #    {:id=>6578954, :name=>"Vicky"}]

请注意,这里update的论点是g[:name]=>g[:id]之前g[:name]=>[g[:id]]的。

第一步如下。

arr.each_with_object({}) do |g,h|
  h.update(g[:name]=>g[:id]) { |_,o,n| [*o,n] }
end
  #=> {"John"=>[3456824, 987456], "Vicky"=>6578954}

一般来说,当可以使用Enumerable#group_by时,可以采用其中一种或两种方法。反之亦然。这些方法中的选择是个人品味的问题。

1. 此计算的第一部分的变体是hashes.each_with_object({}) { |g,h| (h[g[:name]] ||= []) << g[:id] } #=> {"John"=>[3456824, 987456], "Vicky"=>[6578954]}


推荐阅读