Reputation: 391
Say I have an array as such:
array = [["male", 59], ["female", 31], ["unknown gender", 38]]
The number inside the array is the total number of users by gender. What I need to do is get each element of the array and find the average for each male, female and unknown and put them into a new array that looks like this:
new_array = [["male", 46], ["female", 24], ["unknown gender", 30]]
Where the 2nd value inside the tuple is the average.
I have tried setting it as such:
new_array = []
sum = 0
array.each do |k,v|
sum += v
new_array << [k,(v/sum)*100]
end
My new array however turns out to be [["male", 100], ["female", 0], ["unknown gender", 0]]
Ive tried a few different things and cannot figure out what Im doing wrong. Any help would be great.
Upvotes: 0
Views: 108
Reputation: 110645
If you have a choice, I suggest you consider using hashes rather than an arrays and symbols rather than strings:
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}
Upvotes: 0
Reputation: 106782
I would prefer to use more specialised Ruby methods like inject
or map
instead of the simple 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]]
Furthermore the ((v.to_f / total) * 100).round
part can be extracted into its own method like this:
def percentage(value, total)
((value.to_f / total) * 100).round
end
What makes the map
more readable:
new_array = array.map { |k, v| [k, percentage(v, total)] }
Upvotes: 0
Reputation: 12558
There are two issues here.
The first is that you are doing integer division at (v/sum)
. The result of this is probably not what you expect, so you should turn v
into a float: (v.to_f/sum)
.
The second problem is that you are using the sum
variable before it actually contains the sum.
Putting these two together:
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]]
Upvotes: 1
Reputation: 3275
You need to do a few things. what is happening now is that sum is 59. v/59 is one on the first iteration. On the second iteration 31/90 == 0 because you are not dividing the sum by the number of iterations. What you really want is 31/45 because 45 is 90/2(2 being the number of iterations)
1) you need to divide the sum by the number of iterations to get the current average.
2) your syntax of
array.each do |k,v|
is misleading because you are not dealing with keys and values but with array indexes.
Upvotes: 0