从数组中添加哈希
Adding Hashes from an array
我正在尝试从具有相同键和平均值的多个哈希数组创建一个 array/hash。我的数组:
[{:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"}, {:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"}]
现在我想要return这样的东西:
{:amount=>952000, :gross_amount=>952000, :tax=>152000, :hotel_fees=>0, :base_fare=>800000, :currency=>"INR"}
其中值是具有相同键的每个散列值的平均值。
有没有简单的方法可以做到这一点。我试过使用合并,但货币随之变为 0。
我的尝试:
p[0].merge(p[1]){|k,v1,v2| (v1+v2)/2 unless v1 && v2 == "INR"}
编辑:
实际上我的问题并没有就此结束,所以在获得平均值后我需要将值插入另一个散列中。
所以我使用了这样的东西:
price_array = offer_values.map do |v|
v.inject do |k, v|
k.merge!(price: k[:price].merge(v[:price]){|_, a, b| [a, b].flatten })
end
end
price_array.map do |o|
o[:price] = {}.tap{ |h| o[:price].each {|k, list| h[k] = list.all?{|e| [Fixnum, NilClass].include? e.class} ? list.map(&:to_i).sum/list.size : list.compact.first ; h } }
end
其中 offer_array 是我的 orginal/first 数组在单独的散列中的那个。我已经用 2 和 3 个哈希值尝试过,它正在工作。
如果你们有任何改进代码的建议,它是开放的。
IRB
2.2.3 :011 > b = {test1: 30, test2: 40}
=> {:test1=>30, :test2=>40}
2.2.3 :012 > a = {test1: 20, test2: 60}
=> {:test1=>20, :test2=>60}
2.2.3 :013 > c = a.merge(b){|key, oldval, newval| (newval + oldval)/2}
=> {:test1=>25, :test2=>50}
对于数组中的两个散列,您可以使用 inject
和 merge
检查两个 currency
键的值是否为 Fixnum class,如果不是,则取第一个散列中货币 "INR"
的值并使用它:
array = [
{:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"},
{:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"}
]
p array.inject{|k,v| k.merge(v){|_,a,b| [a,b].all?{|e| e.is_a?(Fixnum)} ? (a+b)/2 : b}}
# => {:amount=>952000, :gross_amount=>952000, :tax=>152000, :hotel_fees=>0, :base_fare=>800000, :currency=>"INR"}
对于数组中的两个或多个哈希,您可以尝试:
main_array = [
{:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"},
{:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"},
{:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"},
]
array_result = main_array.flat_map(&:to_a).group_by(&:first).map do |key, array|
{
key => (
result = array.inject(0) do |total, (_, value)|
value.is_a?(Fixnum) ? total + value : value
end
result.is_a?(Fixnum) ? result / main_array.size : result
)
}
end
p array_result
接受的答案不适用于超过 2 个哈希值,因为 merge
仅适用于 2 x 2,而您在这里计算的是平均值。
(((3 + 2) / 2) + 2.5) / 2 is different from (3 + 2 + 2.5) / 3
所以我写了一段代码,可以根据您拥有的任何大小的数组执行您想要的操作
def self.merge_all_and_average(array)
new_hash = {}
unless array.empty?
array[0].keys.each do |key|
if array[0][key].class == Fixnum
total = array.map { |i| i[key] }.inject(0) { |sum, x| sum + x }
new_hash = new_hash.merge(key => total / array.size)
else
new_hash = new_hash.merge(key => array[0][key])
end
end
end
new_hash
end
这应该适用于任意数量的哈希:
data = [
{ amount: 897_500, gross_amount: 897_500, tax: 147_500, hotel_fees: 0, base_fare: 750_000, currency: 'INR' },
{ amount: 1_006_500, gross_amount: 1_006_500, tax: 156_500, hotel_fees: 0, base_fare: 850_000, currency: 'INR' },
{ amount: 1_006_500, gross_amount: 1_006_500, tax: 156_500, hotel_fees: 0, base_fare: 850_000, currency: 'INR' }
]
transposed_hashes = data.each_with_object(Hash.new{|h, k| h[k] = []}) do |h, mem|
h.each do |k, v|
mem[k] << v
end
end
# {:amount=>[897500, 1006500, 1006500], :gross_amount=>[897500, 1006500, 1006500], :tax=>[147500, 156500, 156500], :hotel_fees=>[0, 0, 0], :base_fare=>[750000, 850000, 850000], :currency=>["INR", "INR", "INR"]}
average_hash = transposed_hashes.map do |k, v|
new_value = if v[0].is_a? Integer
v.sum.to_f / v.size
else
v[0]
end
[k, new_value]
end.to_h
puts average_hash
# {:amount=>970166.6666666666, :gross_amount=>970166.6666666666, :tax=>153500.0, :hotel_fees=>0.0, :base_fare=>816666.6666666666, :currency=>"INR"}
我正在尝试从具有相同键和平均值的多个哈希数组创建一个 array/hash。我的数组:
[{:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"}, {:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"}]
现在我想要return这样的东西:
{:amount=>952000, :gross_amount=>952000, :tax=>152000, :hotel_fees=>0, :base_fare=>800000, :currency=>"INR"}
其中值是具有相同键的每个散列值的平均值。
有没有简单的方法可以做到这一点。我试过使用合并,但货币随之变为 0。
我的尝试:
p[0].merge(p[1]){|k,v1,v2| (v1+v2)/2 unless v1 && v2 == "INR"}
编辑:
实际上我的问题并没有就此结束,所以在获得平均值后我需要将值插入另一个散列中。 所以我使用了这样的东西:
price_array = offer_values.map do |v|
v.inject do |k, v|
k.merge!(price: k[:price].merge(v[:price]){|_, a, b| [a, b].flatten })
end
end
price_array.map do |o|
o[:price] = {}.tap{ |h| o[:price].each {|k, list| h[k] = list.all?{|e| [Fixnum, NilClass].include? e.class} ? list.map(&:to_i).sum/list.size : list.compact.first ; h } }
end
其中 offer_array 是我的 orginal/first 数组在单独的散列中的那个。我已经用 2 和 3 个哈希值尝试过,它正在工作。
如果你们有任何改进代码的建议,它是开放的。
IRB
2.2.3 :011 > b = {test1: 30, test2: 40}
=> {:test1=>30, :test2=>40}
2.2.3 :012 > a = {test1: 20, test2: 60}
=> {:test1=>20, :test2=>60}
2.2.3 :013 > c = a.merge(b){|key, oldval, newval| (newval + oldval)/2}
=> {:test1=>25, :test2=>50}
对于数组中的两个散列,您可以使用 inject
和 merge
检查两个 currency
键的值是否为 Fixnum class,如果不是,则取第一个散列中货币 "INR"
的值并使用它:
array = [
{:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"},
{:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"}
]
p array.inject{|k,v| k.merge(v){|_,a,b| [a,b].all?{|e| e.is_a?(Fixnum)} ? (a+b)/2 : b}}
# => {:amount=>952000, :gross_amount=>952000, :tax=>152000, :hotel_fees=>0, :base_fare=>800000, :currency=>"INR"}
对于数组中的两个或多个哈希,您可以尝试:
main_array = [
{:amount=>897500, :gross_amount=>897500, :tax=>147500, :hotel_fees=>0, :base_fare=>750000, :currency=>"INR"},
{:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"},
{:amount=>1006500, :gross_amount=>1006500, :tax=>156500, :hotel_fees=>0, :base_fare=>850000, :currency=>"INR"},
]
array_result = main_array.flat_map(&:to_a).group_by(&:first).map do |key, array|
{
key => (
result = array.inject(0) do |total, (_, value)|
value.is_a?(Fixnum) ? total + value : value
end
result.is_a?(Fixnum) ? result / main_array.size : result
)
}
end
p array_result
接受的答案不适用于超过 2 个哈希值,因为 merge
仅适用于 2 x 2,而您在这里计算的是平均值。
(((3 + 2) / 2) + 2.5) / 2 is different from (3 + 2 + 2.5) / 3
所以我写了一段代码,可以根据您拥有的任何大小的数组执行您想要的操作
def self.merge_all_and_average(array)
new_hash = {}
unless array.empty?
array[0].keys.each do |key|
if array[0][key].class == Fixnum
total = array.map { |i| i[key] }.inject(0) { |sum, x| sum + x }
new_hash = new_hash.merge(key => total / array.size)
else
new_hash = new_hash.merge(key => array[0][key])
end
end
end
new_hash
end
这应该适用于任意数量的哈希:
data = [
{ amount: 897_500, gross_amount: 897_500, tax: 147_500, hotel_fees: 0, base_fare: 750_000, currency: 'INR' },
{ amount: 1_006_500, gross_amount: 1_006_500, tax: 156_500, hotel_fees: 0, base_fare: 850_000, currency: 'INR' },
{ amount: 1_006_500, gross_amount: 1_006_500, tax: 156_500, hotel_fees: 0, base_fare: 850_000, currency: 'INR' }
]
transposed_hashes = data.each_with_object(Hash.new{|h, k| h[k] = []}) do |h, mem|
h.each do |k, v|
mem[k] << v
end
end
# {:amount=>[897500, 1006500, 1006500], :gross_amount=>[897500, 1006500, 1006500], :tax=>[147500, 156500, 156500], :hotel_fees=>[0, 0, 0], :base_fare=>[750000, 850000, 850000], :currency=>["INR", "INR", "INR"]}
average_hash = transposed_hashes.map do |k, v|
new_value = if v[0].is_a? Integer
v.sum.to_f / v.size
else
v[0]
end
[k, new_value]
end.to_h
puts average_hash
# {:amount=>970166.6666666666, :gross_amount=>970166.6666666666, :tax=>153500.0, :hotel_fees=>0.0, :base_fare=>816666.6666666666, :currency=>"INR"}