对多个哈希值求平均值

时间:2014-01-28 20:06:26

标签: ruby hash ruby-on-rails-4

编辑我接受@ CarySwoveland的答案,因为他在第一次尝试时得到了最接近的,占大多数情况,并将数据输出到哈希中,这样您就不需要依赖订购。尽管如此,许多人都可以提及!如果你想要输出数组,请务必查看@ ArupRakshit的答案!

我有一系列哈希,如:

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

我希望数组中每个键的平均值。即。 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 %>

但我觉得可能有更好的方法......任何想法?

5 个答案:

答案 0 :(得分:2)

执行以下操作

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

答案 1 :(得分:1)

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

以下是一个示例数组:

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]

答案 2 :(得分:1)

试试这个

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

如果要从平均值中排除空值,可以更改average_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]

答案 3 :(得分:1)

另一种方式:

@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创建的对象是一个哈希,其默认值为空数组。该哈希由块变量h表示。这意味着,如果h[k] << mh[k].to_f执行h.key?(k) => false,则首先执行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 }

答案 4 :(得分: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