Reputation: 17
I have an array of arrays called intervals. I wish to build an array of hashes out of it, adding two key/value pairs to each hash (start_ts and stop_ts).
require 'date'
date = '2014-06-12'
totalhash = Hash.new
totalarray = Array.new
payload2 = Array.new
totals = Array.new
intervals = [["Current", 0, 9999],
["1 to 4", -4, -1],
["5 to 15", -15, -5],
["16 to 30", -30, -16],
["31 to 60", -60, -31],
["61 to 90", -90, -61],
["91+", -9999, -91]]
intervals.each do |int|
label, start, stop = int
# Parse date and then convert to UNIX epoch (.to_time.to_i chain)
start_ts = (Date.parse("#{date}") + start).to_time.to_i
stop_ts = (Date.parse("#{date}") + stop).to_time.to_i
totalhash[:label] = label
totalhash[:start] = start
totalhash[:stop] = stop
totalhash[:start_ts] = start_ts
totalhash[:stop_ts] = stop_ts
totalarray << totalhash
totals = totalarray.reduce Hash.new, :merge
puts totals
puts 'totals size: ' + totals.size.to_s
end
The end result should be an array of seven hashes. Currently the array totalarray seems to be overwritten on each pass as opposed to being appended to.
What am I doing wrong. Thanks.
Upvotes: 0
Views: 113
Reputation: 37517
When you want a 1-for-1 output from an array, use map
. It reduces the need for all those intermediate variables.
# Parse date outside the loop as per @Uri's comment
day = Date.parse(date)
t = intervals.map do |interval|
label, start, stop = interval
{
label: label,
start: start,
stop: stop,
start_ts: (day + start).to_time.to_i,
stop_ts: (day + stop).to_time.to_i
}
end
This results in your desired seven-hash array.
As for the single hash output you are getting: your reduce
line is the culprit. I'm not sure what you are trying to do there.
Upvotes: 1
Reputation: 110675
I suggest you consider changing your data structure. I don't think it's wise to include the computed times since the epoch in each hash; rather, just compute those values as needed with a helper method:
require 'date'
date = Date.parse('2014-06-12')
#=> #<Date: 2014-06-12 ((2456821j,0s,0n),+0s,2299161j)>
def start_stop_to_time(d, date)
(date + d).to_time.to_i
end
For example,
start_stop_to_time(-4, date) #=> 1402210800
total_array
would then be:
total_array = [[:label, :start, :stop]].product(intervals)
.map { |k,v| k.zip(v).to_h }
#=> [{:label=> "Current", :start=> 0, :stop=>9999},
# {:label=> "1 to 4", :start=> -4, :stop=> -1},
# {:label=> "5 to 15", :start=> -15, :stop=> -5},
# {:label=>"16 to 30", :start=> -30, :stop=> -16},
# {:label=>"31 to 60", :start=> -60, :stop=> -31},
# {:label=>"61 to 90", :start=> -90, :stop=> -61},
# {:label=> "91+", :start=>-9999, :stop=> -91}]
I do not understand the purpose of totals
, so I cannot comment on that.
Upvotes: 0
Reputation: 133
I usually do this sort of thing this way:
myArray = [['cow','moo'],['dog','woof'],['duck','quack'],['fox','?']]
myArray.collect! do |animal|
animal = {animal[0]=>animal[1]}
end
puts myArray.inspect
I am not familiar enough with reduce
or inject
to comment on your use here. But here is an edited version of your original code that I think does what you want:
require 'date'
date = '2014-06-12'
#totalhash = Hash.new
totalarray = Array.new
payload2 = Array.new
totals = Array.new
intervals = [["Current", 0, 9999],
["1 to 4", -4, -1],
["5 to 15", -15, -5],
["16 to 30", -30, -16],
["31 to 60", -60, -31],
["61 to 90", -90, -61],
["91+", -9999, -91]]
intervals.each do |int|
totalhash = Hash.new #moved your hash creation here, in the iteration
label, start, stop = int
# Parse date and then convert to UNIX epoch (.to_time.to_i chain)
start_ts = (Date.parse("#{date}") + start).to_time.to_i
stop_ts = (Date.parse("#{date}") + stop).to_time.to_i
totalhash[:label] = label
totalhash[:start] = start
totalhash[:stop] = stop
totalhash[:start_ts] = start_ts
totalhash[:stop_ts] = stop_ts
totalarray << totalhash
#totals = totalarray.reduce Hash.new, :merge
#puts totals
#puts 'totals size: ' + totals.size.to_s
end
puts totalarray.inspect #see the array object as is using 'inspect'
~
Upvotes: 0
Reputation: 434665
This:
totalarray << totalhash
does not copy totalhash
, it merely appends a reference to the end of totalarray
. It would make a lot more sense to say:
totalarray << {
# build the Hash inline right here
}
Your code ends up with intervals.length
references to exactly the same Hash in totalarray
. Then your reduce
merges that Hash into itself and that does nothing useful. Actually your totals = totalarray.reduce Hash.new, :merge
does nothing useful even if totalarray
is properly built, you could just say totals = totalarray.last
and get the same result.
Upvotes: 0