Ruby 根据匹配键对散列进行分组,并将不匹配键的值存储在数组中
Ruby group hashes based on matching keys and store the value of non matching keys in an array
需要根据下面的哈希值实现给定的输出
foos = [ { :key => 'Foo', :value => 1, :revenue => 2 },
{ :key => 'Foo', :value => 1, :revenue => 4 },
{ :key => 'Bar', :value => 2, :revenue => 7 },
{ :key => 'bar', :value => 2, :revenue => 9 },
{ :key => 'Zampa', :value => 4, :revenue => 9 }]
输出应该是:
{ :key => 'Foo', :value => 1, :revenue => [2,4] } #Merging row 1 & 2 as they share same :key 'Foo'
{ :key => 'Bar', :value => 2, :revenue => [7,9] } #Merging row 3 & 4 as they share same :key 'Bar'
{ :key => 'Zampa', :value => 4, :revenue => 9 }
合并应该基于:Key字段的值
如何在 ruby 中实现这一点,因为我是 ruby.
的新手
您可以尝试按 key
和 value
分组,然后映射 revenue
值:
foos
.group_by { |e| e.values_at(:key, :value) }
.map do |(key, value), values|
{ key: key, value: value, revenue: values.map { |e| e[:revenue] } }
end
# [{:key=>"Foo", :value=>1, :revenue=>[2, 4]}, {:key=>"Bar", :value=>2, :revenue=>[7]}, {:key=>"bar", :value=>2, :revenue=>[9]}, {:key=>"Zampa", :value=>4, :revenue=>[9]}]
使用减少
result=foos.group_by { |x| x[:key] }.values.map do |arr|
arr.reduce do |h1, h2|
h1.merge(h2) do |k, v1, v2|
k.eql?(:revenue) ? [v1, v2] : v1
end
end
end
p 结果
[{:key=>"Foo", :value=>1, :revenue=>[2, 4]}, {:key=>"bar", :value=>2, :revenue=>[7, 9]}, {:key=>"Zampa", :value=>4, :revenue=>9}]
您可以使用 group_by
to group the foos
array by :key
. However I would first downcase
和 :key
值,因为您希望 'Bar'
和 'bar'
最终属于同一组。
# We first need to unify the keys of the hashes before we can start
# grouping. 'Bar' != 'bar' so they would be split up in two separate
# groups. Judging from the output you don't want this.
foos.each { |foo| foo[:key].downcase! }
# Now that all keys are downcased we can group based upon the value of
# the :key key.
grouped_foos = foos.group_by { |foo| foo[:key] }
# Now we need to map over the resulting hash and create a single result
# for each group.
grouped_foos.transform_values! do |foos|
# First I'll transform the structure of `foos`, from:
#
# [{a: 1, b: 2}, {a: 3, b: 4}]
#
# into:
#
# [[:a, 1], [:b, 2], [:a, 3], [:b, 4]]
#
tmp = foos.flat_map(&:to_a)
# Then I'll group the above structure based upon the first value in
# each array, simultaneously removing the first element. Resulting in:
#
# {a: [[1], [3]], b: [[2], [4]]}
#
tmp = tmp.group_by(&:shift)
# We now need to flatten the values by one level. Resulting in:
#
# {a: [1, 3], b: [2, 4]}
#
tmp.transform_values! { |values| values.flatten(1) }
# The next step is remove duplicate values. We currently have:
#
# {key: ['foo', 'foo'], value: [1, 1], revenue: [2, 4]}
#
# whereas we want:
#
# {key: ['foo'], value: [1], revenue: [2, 4]}
#
tmp.transform_values!(&:uniq)
# Lastly if the array only contains a single value we want to use the
# value instead of an array. Transforming the above structure into:
#
# {key: 'foo', value: 1, revenue: [2, 4]}
#
tmp.transform_values! { |head, *tail| tail.empty? ? head : [head, *tail] }
# Finally we need to return our new hash.
tmp
end
结合以上步骤,我们得到以下结果:
foos.each { |foo| foo[:key].downcase! }
grouped_foos = foos.group_by { |foo| foo[:key] }
grouped_foos.transform_values! do |foos|
foos.flat_map(&:to_a).group_by(&:shift)
.transform_values { |values| values.flatten(1).uniq }
.transform_values { |head, *tail| tail.empty? ? head : [head, *tail] }
end
如果您不想修改原始 foos
结构的(大写),您必须替换:
foos.each { |foo| foo[:key].downcase! }
# with
unified_keys = foos.map(&:dup).each { |foo| foo[:key] = foo[:key].downcase }
然后从那时起使用新的 unified_keys
结构。
上述解决方案产生以下结果:
grouped_foos
#=> {"foo" =>{:key=>"foo", :value=>1, :revenue=>[2, 4]},
# "bar" =>{:key=>"bar", :value=>2, :revenue=>[7, 9]},
# "zampa"=>{:key=>"zampa", :value=>4, :revenue=>9}}
你可以通过请求 grouped_foos
:
的值来得到你想要的结果
grouped_foos.values
#=> [{:key=>"foo", :value=>1, :revenue=>[2, 4]},
# {:key=>"bar", :value=>2, :revenue=>[7, 9]},
# {:key=>"zampa", :value=>4, :revenue=>9}]
需要根据下面的哈希值实现给定的输出
foos = [ { :key => 'Foo', :value => 1, :revenue => 2 },
{ :key => 'Foo', :value => 1, :revenue => 4 },
{ :key => 'Bar', :value => 2, :revenue => 7 },
{ :key => 'bar', :value => 2, :revenue => 9 },
{ :key => 'Zampa', :value => 4, :revenue => 9 }]
输出应该是:
{ :key => 'Foo', :value => 1, :revenue => [2,4] } #Merging row 1 & 2 as they share same :key 'Foo'
{ :key => 'Bar', :value => 2, :revenue => [7,9] } #Merging row 3 & 4 as they share same :key 'Bar'
{ :key => 'Zampa', :value => 4, :revenue => 9 }
合并应该基于:Key字段的值 如何在 ruby 中实现这一点,因为我是 ruby.
的新手您可以尝试按 key
和 value
分组,然后映射 revenue
值:
foos
.group_by { |e| e.values_at(:key, :value) }
.map do |(key, value), values|
{ key: key, value: value, revenue: values.map { |e| e[:revenue] } }
end
# [{:key=>"Foo", :value=>1, :revenue=>[2, 4]}, {:key=>"Bar", :value=>2, :revenue=>[7]}, {:key=>"bar", :value=>2, :revenue=>[9]}, {:key=>"Zampa", :value=>4, :revenue=>[9]}]
使用减少
result=foos.group_by { |x| x[:key] }.values.map do |arr|
arr.reduce do |h1, h2|
h1.merge(h2) do |k, v1, v2|
k.eql?(:revenue) ? [v1, v2] : v1
end
end
end
p 结果
[{:key=>"Foo", :value=>1, :revenue=>[2, 4]}, {:key=>"bar", :value=>2, :revenue=>[7, 9]}, {:key=>"Zampa", :value=>4, :revenue=>9}]
您可以使用 group_by
to group the foos
array by :key
. However I would first downcase
和 :key
值,因为您希望 'Bar'
和 'bar'
最终属于同一组。
# We first need to unify the keys of the hashes before we can start
# grouping. 'Bar' != 'bar' so they would be split up in two separate
# groups. Judging from the output you don't want this.
foos.each { |foo| foo[:key].downcase! }
# Now that all keys are downcased we can group based upon the value of
# the :key key.
grouped_foos = foos.group_by { |foo| foo[:key] }
# Now we need to map over the resulting hash and create a single result
# for each group.
grouped_foos.transform_values! do |foos|
# First I'll transform the structure of `foos`, from:
#
# [{a: 1, b: 2}, {a: 3, b: 4}]
#
# into:
#
# [[:a, 1], [:b, 2], [:a, 3], [:b, 4]]
#
tmp = foos.flat_map(&:to_a)
# Then I'll group the above structure based upon the first value in
# each array, simultaneously removing the first element. Resulting in:
#
# {a: [[1], [3]], b: [[2], [4]]}
#
tmp = tmp.group_by(&:shift)
# We now need to flatten the values by one level. Resulting in:
#
# {a: [1, 3], b: [2, 4]}
#
tmp.transform_values! { |values| values.flatten(1) }
# The next step is remove duplicate values. We currently have:
#
# {key: ['foo', 'foo'], value: [1, 1], revenue: [2, 4]}
#
# whereas we want:
#
# {key: ['foo'], value: [1], revenue: [2, 4]}
#
tmp.transform_values!(&:uniq)
# Lastly if the array only contains a single value we want to use the
# value instead of an array. Transforming the above structure into:
#
# {key: 'foo', value: 1, revenue: [2, 4]}
#
tmp.transform_values! { |head, *tail| tail.empty? ? head : [head, *tail] }
# Finally we need to return our new hash.
tmp
end
结合以上步骤,我们得到以下结果:
foos.each { |foo| foo[:key].downcase! }
grouped_foos = foos.group_by { |foo| foo[:key] }
grouped_foos.transform_values! do |foos|
foos.flat_map(&:to_a).group_by(&:shift)
.transform_values { |values| values.flatten(1).uniq }
.transform_values { |head, *tail| tail.empty? ? head : [head, *tail] }
end
如果您不想修改原始 foos
结构的(大写),您必须替换:
foos.each { |foo| foo[:key].downcase! }
# with
unified_keys = foos.map(&:dup).each { |foo| foo[:key] = foo[:key].downcase }
然后从那时起使用新的 unified_keys
结构。
上述解决方案产生以下结果:
grouped_foos
#=> {"foo" =>{:key=>"foo", :value=>1, :revenue=>[2, 4]},
# "bar" =>{:key=>"bar", :value=>2, :revenue=>[7, 9]},
# "zampa"=>{:key=>"zampa", :value=>4, :revenue=>9}}
你可以通过请求 grouped_foos
:
grouped_foos.values
#=> [{:key=>"foo", :value=>1, :revenue=>[2, 4]},
# {:key=>"bar", :value=>2, :revenue=>[7, 9]},
# {:key=>"zampa", :value=>4, :revenue=>9}]