Лучший способ заполнить пробелы в многомерном массиве в Ruby - PullRequest
3 голосов
/ 26 января 2011

У меня есть многомерный массив, аналогичный приведенному ниже примеру, который я хочу сгруппировать, используя метод zip в Ruby.У меня это работает нормально, когда каждый внутренний массив имеет одинаковое количество элементов, но сталкиваюсь с проблемами, когда они имеют разную длину.

В приведенном ниже примере во втором наборе отсутствует запись в 00:15. Как бы я заполнил эту пропущенную запись?

Что я рассматриваю пробел?

Это отметка времени, которая составляет пробел,Взгляните на мой первый пример кода, где у меня есть комментарий о пробеле в 00:15.Все другие массивы имеют хэш с этой отметкой времени, поэтому я считаю, что это «отсутствующая запись» или «пробел».Отметка времени действительно может быть какой-то другой уникальной строкой, поэтому тот факт, что они находятся на расстоянии 15 минут, не имеет значения.Значения также не имеют значения.

Единственный подход, который приходит на ум, заключается в повторении циклов над массивами дважды.Первый раз - создать массив уникальных временных меток, а второй - заполнить пропущенные записи, в которых временной метки нет.Мне удобно кодировать этот подход, но он кажется немного странным, и Руби всегда удивляет меня элегантным и лаконичным решением.

Я начинаю с этого:

values = [
  [
    {:timestamp => "2011-01-01 00:00", :value => 1},
    {:timestamp => "2011-01-01 00:15", :value => 2},
    {:timestamp => "2011-01-01 00:30", :value => 3}
  ],
  [ # There's a gap here at 00:15
    {:timestamp => "2011-01-01 00:00", :value => 1},
    {:timestamp => "2011-01-01 00:30", :value => 3}
  ],
  [
    {:timestamp => "2011-01-01 00:00", :value => 1},
    {:timestamp => "2011-01-01 00:15", :value => 2},
    {:timestamp => "2011-01-01 00:30", :value => 3}
  ]
]

Я хочув итоге:

values = [
  [
    {:timestamp => "2011-01-01 00:00", :value => 1},
    {:timestamp => "2011-01-01 00:15", :value => 2},
    {:timestamp => "2011-01-01 00:30", :value => 3}
  ],
  [ # The gap has been filled with a nil value
    {:timestamp => "2011-01-01 00:00", :value => 1},
    {:timestamp => "2011-01-01 00:15", :value => nil},
    {:timestamp => "2011-01-01 00:30", :value => 3}
  ],
  [
    {:timestamp => "2011-01-01 00:00", :value => 1},
    {:timestamp => "2011-01-01 00:15", :value => 2},
    {:timestamp => "2011-01-01 00:30", :value => 3}
  ]
]

Когда все массивы имеют одинаковый размер, values.transpose выдаст:

[
  [
   {:value=>1, :timestamp=>"2011-01-01 00:00"}, 
   {:value=>1, :timestamp=>"2011-01-01 00:00"}, 
   {:value=>1, :timestamp=>"2011-01-01 00:00"}
  ], 
  [
    {:value=>2, :timestamp=>"2011-01-01 00:15"}, 
    {:value=>nil, :timestamp=>"2011-01-01 00:15"},
    {:value=>2, :timestamp=>"2011-01-01 00:15"}
  ], 
  [
    {:value=>3, :timestamp=>"2011-01-01 00:30"}, 
    {:value=>3, :timestamp=>"2011-01-01 00:30"}, 
    {:value=>3, :timestamp=>"2011-01-01 00:30"}
  ]
]

Ответы [ 3 ]

1 голос
/ 26 января 2011

Вот рабочее решение; он находит все временные метки, находит недостающие временные метки в каждом наборе, а затем вводит их. См. Комментарии после решения для небольшого улучшения, которое вы могли бы сделать с Ruby 1.9.2:

values = [[
  {:timestamp => "2011-01-01 00:00", :value => 1},
  {:timestamp => "2011-01-01 00:15", :value => 2},
  {:timestamp => "2011-01-01 00:30", :value => 3}
],[
  {:timestamp => "2011-01-01 00:00", :value => 1},
  {:timestamp => "2011-01-01 00:30", :value => 3}
],[
  {:timestamp => "2011-01-01 00:00", :value => 1},
  {:timestamp => "2011-01-01 00:15", :value => 2},
  {:timestamp => "2011-01-01 00:30", :value => 3}
]]

all_stamps = values.flatten.map{|x| x[:timestamp]}.uniq.sort
values.each do |set|
  my_stamps = set.map{ |x| x[:timestamp] }.uniq
  missing   = all_stamps - my_stamps
  set.concat( missing.map{ |stamp| {timestamp:stamp, value:nil} } )
  set.replace( set.sort_by{ |x| x[:timestamp] } )
end

require 'pp'
pp values
#=> [[{:timestamp=>"2011-01-01 00:00", :value=>1},
#=>   {:timestamp=>"2011-01-01 00:15", :value=>2},
#=>   {:timestamp=>"2011-01-01 00:30", :value=>3}],
#=>  [{:timestamp=>"2011-01-01 00:00", :value=>1},
#=>   {:timestamp=>"2011-01-01 00:15", :value=>nil},
#=>   {:timestamp=>"2011-01-01 00:30", :value=>3}],
#=>  [{:timestamp=>"2011-01-01 00:00", :value=>1},
#=>   {:timestamp=>"2011-01-01 00:15", :value=>2},
#=>   {:timestamp=>"2011-01-01 00:30", :value=>3}]]

В Ruby 1.9.2 вы можете заменить set.replace( set.sort_by{...} ) на set.sort_by!{ ... }. Также обратите внимание, что я предположил, что вы используете Ruby 1.9 в моем хеш-литерале (см. missing.map...).

1 голос
/ 26 января 2011

Подход, который вы описали, верен, но оказывается, что ruby ​​очень хорошо подходит для такого элегантного подхода.Это будет сделано, например:

stamps = values.map{ |logs| logs.map{ |row| row[:timestamp] } }.flatten.uniq.sort
values.map!{ |logs| stamps.map { |ts| logs.select{ |row| row[:timestamp] == ts }.first || { :timestamp => ts, :value => nil } } }

Первая строка получает список уникальных временных меток (отображает все журналы в просто массивы временных меток, объединяет массивы в один массив, сохраняет только уникальные исортирует метки времени).

Вторая строка заполняет пробелы (циклически просматривает журналы, и для каждой метки времени в этом журнале используйте то, что есть, если что-то есть, в противном случае вставьте новую строку с нулевым значением).

0 голосов
/ 03 февраля 2011

Также проверьте Array#in_groups_of, если вы используете Rails

%w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g}
["1", "2", "3"]
["4", "5", "6"]
["7", nil, nil]

http://weblog.rubyonrails.org/2006/3/1/new-in-rails-enumerable-group_by-and-array-in_groups_of

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...