Ruby: добавление хешей к массиву в каждом цикле

У меня есть массив массивов, называемых интервалами. Я хочу построить из него массив хэшей, добавив к каждому хешу две пары ключ/значение (start_ts и 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

Конечным результатом должен быть массив из семи хэшей. В настоящее время массив totalarray, по-видимому, перезаписывается при каждом проходе, а не добавляется к нему.

Что я делаю не так. Спасибо.


person user3707736    schedule 12.06.2014    source источник
comment
Как вы запускали этот код? Слишком много синтаксических ошибок..   -  person Arup Rakshit    schedule 12.06.2014
comment
@ArupRakshit Я вижу только одну синтаксическую ошибку: intervals.each do |int| do (дважды do).   -  person Jordan Running    schedule 12.06.2014
comment
Я добавил требование «дата» и удалил оскорбительное второе действие. Прости за это.   -  person user3707736    schedule 12.06.2014
comment
@Jordan Если вы побежите, вы получите еще один с предыдущим кодом OP. Но после этого я его не запускал..   -  person Arup Rakshit    schedule 12.06.2014
comment
Можете ли вы объяснить, что представляет собой totals?   -  person Cary Swoveland    schedule 12.06.2014


Ответы (4)


Если вам нужен вывод 1-к-1 из массива, используйте map. Это уменьшает потребность во всех этих промежуточных переменных.

# 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

В результате вы получите желаемый массив из семи хэшей.

Что касается одного вывода хэша, который вы получаете: ваша строка reduce является виновником. Я не уверен, что вы пытаетесь там сделать.

person Mark Thomas    schedule 12.06.2014
comment
Все это хорошие ответы, но я принимаю это, поскольку это было для меня самым ясным. Спасибо, Марк. - person user3707736; 12.06.2014
comment
Хороший вопрос @UriAgassi, я включил ваш комментарий. Спасибо. - person Mark Thomas; 13.06.2014

Этот:

totalarray << totalhash

не копирует totalhash, а просто добавляет ссылку в конец totalarray. Гораздо разумнее было бы сказать:

totalarray << {
  # build the Hash inline right here
}

Ваш код заканчивается intervals.length ссылками на точно такой же хэш в totalarray. Затем ваш reduce объединяет этот хэш сам с собой, и это не делает ничего полезного. На самом деле ваш totals = totalarray.reduce Hash.new, :merge не делает ничего полезного, даже если totalarray правильно построен, вы можете просто сказать totals = totalarray.last и получить тот же результат.

person mu is too short    schedule 12.06.2014

Я обычно делаю такие вещи так:

myArray = [['cow','moo'],['dog','woof'],['duck','quack'],['fox','?']]

myArray.collect! do |animal|
    animal = {animal[0]=>animal[1]}
end

puts myArray.inspect

Я недостаточно знаком с reduce или inject, чтобы комментировать ваше использование здесь. Но вот отредактированная версия вашего исходного кода, которая, я думаю, делает то, что вы хотите:

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'

~

person DMS    schedule 12.06.2014

Я предлагаю вам подумать об изменении структуры данных. Я не думаю, что разумно включать вычисленное время с начала эпохи в каждый хэш; скорее, просто вычислите эти значения по мере необходимости с помощью вспомогательного метода:

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  

Например,

start_stop_to_time(-4, date) #=> 1402210800

total_array тогда будет:

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}]

Я не понимаю цели totals, поэтому не могу это комментировать.

person Cary Swoveland    schedule 12.06.2014