基于匹配键的Ruby组哈希,将不匹配键的值存储在数组中

问题描述 投票:0回答:3

需要基于以下哈希值实现给定的输出

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 hash ruby-hash
3个回答
0
投票

您可以尝试按keyvalue分组,然后映射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]}]

0
投票

使用减少

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

0
投票

您可以使用group_bygroup_by阵列按foos分组。但是,我想先将:key值设为downcase,因为您希望downcase:key结束于同一组。

'Bar'

结合以上步骤,我们得到以下结果:

'bar'

如果您不想修改原始的# We first need to unify the keys of the hashes before we can start # grouping. 'Foo' != 'foo' 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}}
© www.soinside.com 2019 - 2024. All rights reserved.