在多个哈希之间进行平均值



edit 我接受 @caryswoveland的答案,因为他在第一次尝试中最接近的是最接近的,占最多的方案,并将数据输出到哈希您不需要依靠订单。不过许多荣誉提及!如果要在数组中输出,请务必查看 @aruprakshit的答案!

我有一系列哈希,例如:

@my_hashes = [{"key1" => "10", "key2" => "5"...},{"key1" => "", "key2" => "9"...},{"key1" => "6", "key2" => "4"...}]

我想要整个数组中的每个键的平均值。IE。8.0,6.0...

请注意,即使键的值为空白,哈希都具有完全相同的键。现在有效:

<%= @my_hashes[0].keys.each do |key| %>
    <% sum = 0 %>
    <% count = 0 %>
    <% @my_hashes.each do |hash| %>
        <% sum += hash[key].to_f %>
        <% count += if hash[key].blank? then 0 else 1 end  %>       
    <% end %>
    <%= (sum/count) %>
<% end %>

但是我觉得有更好的方法...有什么想法吗?

做以下

@my_hashes = [{"key1" => "10", "key2" => "5"},{"key1" => "", "key2" => "9"},{"key1" => "6", "key2" => "4"}]
ar = @my_hashes[0].keys.map do |k|
   a = @my_hashes.map { |h| h[k].to_f unless h[k].blank? }.compact
   a.inject(:+)/a.size unless a.empty? #Accounting for "key1" => nil or "key1" => ""
end
ar # => [8, 6]

另一种方式:

@my_hashes = [ {"key1"=>"10", "key2"=>"5"},
               {"key1"=>  "", "key2"=>"9"},
               {"key1"=> "6", "key2"=>"4"} ]
def avg(arr) arr.any? ? arr.reduce(:+)/arr.size.to_f : 0.0 end
(@my_hashes.each_with_object ( Hash.new { |h,k| h[k]=[] } ) {
  |mh,h| mh.keys.each { |k| h[k] << mh[k].to_f unless mh[k].empty? } })
           .each_with_object({}) { |(k,v),h| h[k] = avg(v) }
  # => {"key1"=>8.0, "key2"=>6.0}

第一个each_with_object创建的对象是Hash,其默认值是一个空数组。哈希由块变量h表示。这意味着,如果要在h.key?(k) => false时执行h[k] << mh[k].to_f,则首先执行h[k] = []

一个人可以删除avg方法并在计算平均值之前创建一个临时变量:

h = @my_hashes.each_with_object ( Hash.new { |h,k| h[k]=[] } ) { |mh,h|
      mh.keys.each { |k| h[k] << mh[k].to_f unless mh[k].empty? } }
h.each_with_object({}) { |(k,v),h|
  h[k] = ( avg(v) arr.any? ? arr.reduce(:+)/arr.size.to_f : 0.0 }

我想我找到了一个非常优雅的解决方案。

这是一个示例数组:

a = [
  {:a => 2, :b => 10},
  {:a => 4, :b => 20},
  {:a => 2, :b => 10},
  {:a => 8, :b => 40},
]

和解决方案:

class Array
  def average
    self.reduce(&:+) / self.size
  end
end
r = a[0].keys.map do |key|
  [key, a.map { |hash| hash[key] }.average]
end
puts Hash[*r.flatten]

尝试此

@my_hashes = [{"key1" => "10", "key2" => "5"},{"key1" => "", "key2" => "9"},{"key1" => "6", "key2" => "4"}]
average_values = @my_hashes.map(&:values).transpose.map { |arr| 
    arr.map(&:to_f).inject(:+) / arr.size 
}
with_keys = Hash[@my_hashes.first.keys.zip(average_values)]

average_values # =>  [5.333333333333333, 6.0]
with_keys # => {"key1"=>5.333333333333333, "key2"=>6.0}

如果要从平均值中排除空值,则可以将平均_values更改为拒绝空值

average_values = @my_hashes.map(&:values).transpose.map { |arr| 
    arr.reject!(&:empty?)
    arr.map(&:to_f).inject(:+) / arr.size 
}
average_values # => [8.0, 6.0]

没有超级干净的解决方案,但我会写:

a = [
  {:a => 2, :b => 10},
  {:a => 4, :b => 20},
  {:a => 2, :b => 10},
  {:a => 8, :b => 40},
]
grouped = a.flat_map(&:to_a).group_by{|x,|x}
grouped.keys.each do |key|
  len = grouped[key].size
  grouped[key] = 1.0 * grouped[key].map(&:last).inject(:+) / len
end

最新更新