Ruby的哈希数组,比较2个键并加上另一个键/值

时间:2013-08-24 17:46:52

标签: ruby arrays hash

在Ruby中,我有以下哈希数组:

[
  {:qty => 1, :unit => 'oz', :type => 'mass'},
  {:qty => 5, :unit => 'oz', :type => 'vol'},
  {:qty => 4, :unit => 'oz', :type => 'mass'},
  {:qty => 1, :unit => 'lbs', :type => 'mass'}
]

我需要做的是按:unit:type比较元素,然后在它们相同时对:qty求和。生成的数组应如下所示:

[
  {:qty => 5, :unit => 'oz', :type => 'mass'},
  {:qty => 5, :unit => 'oz', :type => 'vol'},
  {:qty => 1, :unit => 'lbs', :type => 'mass'}
]

如果数组有多个哈希值,:qtynil:unit为空(""),那么它只会返回其中一个哈希值。所以为了扩展上面的例子,这个:

[
  {:qty => 1, :unit => 'oz', :type => 'mass'},
  {:qty => nil, :unit => '', :type => 'Foo'},
  {:qty => 5, :unit => 'oz', :type => 'vol'},
  {:qty => 4, :unit => 'oz', :type => 'mass'},
  {:qty => 1, :unit => 'lbs', :type => 'mass'},
  {:qty => nil, :unit => '', :type => 'Foo'}
]

会变成这样:

[
  {:qty => 5, :unit => 'oz', :type => 'mass'},
  {:qty => nil, :unit => '', :type => 'Foo'},
  {:qty => 5, :unit => 'oz', :type => 'vol'},
  {:qty => 1, :unit => 'lbs', :type => 'mass'}
]

编辑:对不起,在第二个例子中犯了一个错误......它不应该有o。

3 个答案:

答案 0 :(得分:8)

首先将group_by与您想要的密钥一起使用,然后reduce将每个值中的qty分成一个哈希值,或者如果它们都是nil则使用nil properties.group_by do |property| property.values_at :type, :unit end.map do |(type, unit), properties| quantities = properties.map { |p| p[:qty] } qty = quantities.all? ? quantities.reduce(:+) : nil { type: type, unit: unit, qty: qty } end #=> [{:type=>"mass", :unit=>"oz", :qty=>5}, # {:type=>"Foo", :unit=>"", :qty=>nil}, # {:type=>"vol", :unit=>"oz", :qty=>5}, # {:type=>"mass", :unit=>"lbs", :qty=>1}]

properties

{{1}}是您的第二个样本输入数据。

答案 1 :(得分:3)

您将需要enumberable.group_by

这应该让你入门

items.group_by { |item| item.values_at(:unit, :type) }

输出

{
  ["oz", "mass"]=> [
    {:qty=>1, :unit=>"oz", :type=>"mass"},
    {:qty=>4, :unit=>"oz", :type=>"mass"}
  ],
  ["oz", "vol"]=>[
    {:qty=>5, :unit=>"oz", :type=>"vol"}
  ],
  ["lbs", "mass"]=>[
    {:qty=>1, :unit=>"lbs", :type=>"mass"}
  ]
}

答案 2 :(得分:-1)

ar = [{:qty => 1, :unit => 'oz', :type => 'mass'}, {:qty => nil, :unit => '', :type => 'Foo'}, 
      {:qty => 5, :unit => 'oz', :type => 'vol'}, 
      {:qty => 4, :unit => 'oz', :type => 'mass'}, {:qty => 1, :unit => 'lbs', :type => 'mass'}, 
      {:qty => nil, :unit => 'o', :type => 'Foo'}]

result = ar.each_with_object(Hash.new(0)) do |e,hsh|
    if hsh.has_key?({:unit => e[:unit], :type => e[:type]})
        hsh[{:unit => e[:unit], :type => e[:type]}] += e[:qty]
    else
        hsh[{:unit => e[:unit], :type => e[:type]}] = e[:qty]
    end
end

result.map{|k,v| k[:qty] = v;k }.delete_if{|h| h[:qty].nil? and !h[:unit].empty? }
# => [{:unit=>"oz", :type=>"mass", :qty=>5},
#     {:unit=>"", :type=>"Foo", :qty=>nil},
#     {:unit=>"oz", :type=>"vol", :qty=>5},
#     {:unit=>"lbs", :type=>"mass", :qty=>1}]

考虑 @Andrew Marshall

ar = [{:qty => 1, :unit => 'oz', :type => 'mass'}, {:qty => nil, :unit => '', :type => 'Foo'}, 
      {:qty => 5, :unit => 'oz', :type => 'vol'}, 
      {:qty => 4, :unit => 'oz', :type => 'mass'}, {:qty => 1, :unit => 'lbs', :type => 'mass'}, 
      {:qty => nil, :unit => 'o', :type => 'Foo'}]

result = ar.each_with_object(Hash.new(0)) do |e,hsh|
    if hsh.has_key?({:unit => e[:unit], :type => e[:type]})
        hsh[{:unit => e[:unit], :type => e[:type]}] += e[:qty]
    else
        hsh[{:unit => e[:unit], :type => e[:type]}] = e[:qty]
    end
end

result.map{|k,v| k[:qty] = v;k }.delete_if{|h| h[:qty].nil? and h[:unit].empty? }
# => [{:unit=>"oz", :type=>"mass", :qty=>5},
#     {:unit=>"oz", :type=>"vol", :qty=>5},
#     {:unit=>"lbs", :type=>"mass", :qty=>1},
#     {:unit=>"o", :type=>"Foo", :qty=>nil}]