将Ruby数组转换为平均值

时间:2014-11-03 00:09:17

标签: ruby arrays

假设我有一个数组:

array = [["male", 59], ["female", 31], ["unknown gender", 38]] 

数组中的数字是按性别划分的用户总数。我需要做的是获取数组的每个元素,找到每个男性,女性和未知的平均值,并将它们放入一个如下所示的新数组中:

new_array = [["male", 46], ["female", 24], ["unknown gender", 30]] 

元组内的第二个值是平均值。

我尝试过这样设置:

new_array = []
sum = 0
array.each do |k,v|
sum += v
new_array << [k,(v/sum)*100]
end

我的新阵列结果却是[["male", 100], ["female", 0], ["unknown gender", 0]]

我尝试了一些不同的东西,无法弄清楚我做错了什么。任何帮助都会很棒。

4 个答案:

答案 0 :(得分:1)

这里有两个问题。

首先是你在(v/sum)进行整数除法。结果可能不是您所期望的,因此您应该将v变为浮动:(v.to_f/sum)

第二个问题是,在实际包含总和之前,您正在使用sum变量。

将这两者放在一起:

array = [["male", 59], ["female", 31], ["unknown gender", 38]] 
new_array = []
sum = 0
# calculating sum before it's used in division below.
array.each { |_, v| sum += v }

array.each do |k, v|
  new_array << [k, ((v.to_f / sum) * 100).round]
end
# => [["male", 46], ["female", 24], ["unknown gender", 30]]

答案 1 :(得分:0)

你需要做一些事情。现在发生的事情是总和是59. v / 59是第一次迭代中的一个。在第二次迭代31/90 == 0,因为您没有将总和除以迭代次数。你真正想要的是31/45,因为45是90/2(2是迭代次数)

1)您需要将总和除以迭代次数以获得当前平均值。

2)你的语法

array.each do |k,v|

具有误导性,因为您不处理键和值,而是处理数组索引。

答案 2 :(得分:0)

我更喜欢使用更专业的Ruby方法,例如injectmap,而不是简单的each

array = [['male', 59], ['female', 31], ['unknown gender', 38]] 

total = array.inject(0) { |sum, (_, n)| sum + n }

new_array = array.map { |k, v| [k, ((v.to_f / total) * 100).round] }
#=> [["male", 46], ["female", 24], ["unknown gender", 30]]

此外,((v.to_f / total) * 100).round部分可以像这样提取到自己的方法中:

def percentage(value, total)
  ((value.to_f / total) * 100).round
end

是什么让map更具可读性:

new_array = array.map { |k, v| [k, percentage(v, total)] }

答案 3 :(得分:0)

如果您有选择,我建议您考虑使用哈希而不是数组和符号而不是字符串:

hash = { male: 59, female: 31, unknown_gender: 38 }
  #=> {:male=>59, :female=>31, :unknown_gender=>38}
tot = hash.values.reduce(:+)
  #=> 128
percents = hash.each_with_object({}) { |(k,v),h| h[k] = (100.0*v/tot).round }
  #=> {:male=>46, :female=>24, :unknown_gender=>30}