从数组中添加哈希值

4
我将尝试从具有相同键的多个哈希的数组中创建一个数组/哈希,并计算值的平均值。我的数组:
[{: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=>952000, :gross_amount=>952000, :tax=>152000, :hotel_fees=>0, :base_fare=>800000, :currency=>"INR"}

在这里,值是每个具有相同键的哈希中值的平均值。

有没有一种简单的方法来做到这一点。我尝试使用merge,但货币变为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是包含原始/第一个数组的单独哈希值的数组。我已经尝试过使用2个和3个哈希值进行操作,它可以正常工作。

如果您有任何改进代码的建议,欢迎提出。


1
我已经尝试使用合并 - 你能展示一下你的尝试吗? - Stefan
合并返回以下内容 {:amount=>952000, :gross_amount=>952000, :tax=>152000, :hotel_fees=>0, :base_fare=>800000, :currency=>nil} - Ravi
我不能使用空的货币。 - Ravi
我的尝试:p[0].merge(p[1]){|k,v1,v2| (v1+v2)/2 unless v1 && v2 == "INR"} - Ravi
在评论中阅读代码有点困难,您是否可以编辑您的问题呢? - Stefan
4个回答

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} 

哦,我以为是其他的东西。 - Ravi

2

接受的答案不能用于超过2个哈希值,因为merge只能两两合并,而您在这里计算平均值。

(((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

我知道它对于超过两个哈希表是行不通的。我只是想知道如何处理它。我一开始走在正确的道路上,但无法得到正确的答案。我的问题从未完全不同,还涉及其他各种事情。这只是其中一个让我感到困惑的部分。我使用的解决方案与你的有些相似。请查看我的编辑。 - Ravi

2

这应该适用于任何数量的哈希:

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

我也在做类似的事情,但这种方法增加了我的代码复杂度。 - Ravi

1
对于数组中的两个哈希值,您可以使用injectmerge,检查两个currency键的值是否为Fixnum类,如果不是,则取第一个哈希中的"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

|_,a,b| 中的 _ 是什么作用?你能解释一下吗? - Ravi
@Ravi,我的答案已经解释了,第一个属性是键,他称之为“_”,第二个属性是旧值或来自第一个哈希的值,第三个属性是新值或来自第二个哈希的值。 - Ramon Marques
_ 对应于哈希键,它可以被命名为任何你想要的名称,但是由于我没有使用它,但必须引用它,所以我将其分配为 _,这意味着它是使块正常工作所需的内容,但不会在内部使用。 - Sebastián Palma
问题提到的是“多个哈希”,而不仅仅是两个哈希。 - Eric Duminil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接