Reputation: 145
I have the following sample data.
Basically I need to divide each data in the variable distribuitions by the data field in the variable entrance.
distribuitions = [{:name=>"cardio", :data=>[["06", 0], ["07", 0], ["08", 0], ["09", 0], ["10", 0], ["11", 0.39e2], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]},
{:name=>"shape", :data=>[["06", 0], ["07", 0], ["08", 0], ["09", 0], ["10", 0], ["11", 0.394e2], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}]
entrance = {:name=>"gym_entrance", :data=>[["06", 0], ["07", 0], ["08", 0], ["09", 0], ["10", 0], ["11", 0.176e3], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}
I have written this function but I can't prevent 0 division.
def divide_distribuition_set_by(distribuitions, d)
distribuitions.each do |distribuition|
collection = [distribuition[:data], d]
hours, attendance = collection.map { |x| x.transpose }.transpose
hours.first.zip(attendance.transpose.map { |col| col.reduce(:/) })
end
end
Expected result ( all values of data are divided by the field data in the entrance variable
[{:name=>"cardio", :data=>[["06", 0], ["07", 0], ["08", 0], ["09", 0], ["10", 0], ["11", 0.2215909090909091,], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]},
{:name=>"shape", :data=>[["06", 0], ["07", 0], ["08", 0], ["09", 0], ["10", 0], ["11", 0.22386363636363635,], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}]
Upvotes: 0
Views: 517
Reputation: 110685
I assume that 0/0
is to be regarded as zero. For n/0
I will return :zd
when n
is non-zero.
distros = [{:name=>"cardio", :data=>[["06", 0], ["07", 3], ["08", 2]]},
{:name=>"shape", :data=>[["06", 0], ["07", 2], ["08", 1]]}]
entrance = {:name=>"gym", :data=>[["08", 2], ["07", 0], ["06", 0]]}
First create a hash g
from entrance[:data]
.
g = entrance[:data].to_h
#=> {"06"=>0, "07"=>0, "08"=>2}
Next, prepare a helper method.
def divide_em(num, den)
if den.zero?
num.zero? ? 0.0 : :zd
else
num.to_f/den
end
end
divide_em(1, 2) #=> 0.5
divide_em(0, 0) #=> 0.0
divide_em(3, 0) #=> :zd
Lastly, perform the divisions.
distros.map { |h| h.merge(h) { |k,d,_| k==:data ?
d.map { |id, num| divide_em(num, g[id]) } : d } }
#=> [{:name=>"cardio", :data=>[0, :zd, 1.0]},
# {:name=>"shape", :data=>[0, :zd, 0.5]}]
Note: 1) the order of the arrays in entrance[:data]
need not correspond to the order of the arrays in each h[:data]
where h
is an element of distros
; and 2) distros
remains unchanged.
This employs the version of Hash#merge that uses a block to determine the values of keys that are present in both hashes being merged, which here is of course all keys. See the doc for details, particularly the definitions of the resolution block's three variables (here k
, d
and _
, the underscore indicating that the third block variable is not used).
Upvotes: 0
Reputation: 80065
Quick and dirty: keep everything as it is, only replace this line
hours.first.zip(attendance.transpose.map { |col| col.reduce(:/) })
by this
hours.first.zip(attendance.transpose.map { |col| col.reduce(:/) rescue 0 })
Upvotes: 1
Reputation: 121000
edata = entrance[:data].to_h
distribuitions.map do |hash|
hash.map do |k, v|
[k, if k == :data
v.to_h.merge(edata) { |_, v1, v2| v2.zero? ? 0 : v1.to_f/v2.to_f }
else
v
end]
end.to_h
end
Upvotes: 0