0

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.

5
  • How you ran this code? It has too many syntax error.. Commented Jun 12, 2014 at 16:57
  • @ArupRakshit I only see one syntax error: intervals.each do |int| do (do twice). Commented Jun 12, 2014 at 16:59
  • I added require 'date' and removed the offending second do. Sorry about that. Commented Jun 12, 2014 at 17:11
  • @Jordan If you run, you will get another one, with OP's previous code. But after that, I didn't run it.. Commented Jun 12, 2014 at 17:17
  • Can you explain what totals is intended to represent? Commented Jun 12, 2014 at 17:31

4 Answers 4

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

These are all good answers but I'm accepting this as it was clearest to me. Thanks, Mark.
Good point @UriAgassi, I've incorporated your comment. Thanks.
0

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.

Comments

0

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 injectto 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'

~

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.