2016-11-08 2 views
1

У меня есть дата массива, содержащие 31 элементов (сокращенных здесь для ясности)Ruby: построить хэш с помощью отображения массивов в массив

Tuesday 8, November 
Wednesday 9, November 
#etc. 

и А фильмов хэша, содержащих 31 элементов, каждый из которых содержит название фильмов и расписание сеансов (укороченные здесь для ясности)

{:movie=>"The Neon Demon", :time=>"4:15 PM"} 
{:movie=>"Breaking a Monster", :time=>"6:45 PM"} 
{:movie=>"The Citizen", :time=>"9:00 PM"} 

Я пытаюсь построить хэш для вывода JSON, который имеет такую ​​структуру

[ 
    { 
    "Tuesday 8, November": { 
     "movies": [ 
     { 
      "movie": "The Neon Demon", 
      "time": "4:15 PM" 
     }, 
     { 
      "movie": "Breaking a Monster", 
      "time": "6:45 PM" 
     }, 
     { 
      "movie": "The Citizen", 
      "time": "9:00 PM" 
     } 
     ] 
    }, 
    { 
    "Wednesday 9, November": { 
     "movies": [ 
     { 
      "movie": "The First Monday in May", 
      "time": "4:15 PM" 
     }, 
     { 
      "movie": "The Neon Demon", 
      "time": "6:30 PM" 
     }, 
     { 
      "movie": "Breaking a Monster", 
      "time": "9:00 PM" 
     } 
     ] 
    }, #etc 
    } 
] 

Прямо сейчас я использую

Output = [] 
Output << { dates => { movies: movies }} 
puts JSON.pretty_generate Output 

Но его не устраивая элементы правильно, и я не могу понять, что лучший способ сделать это. Может ли кто-нибудь дать мне подсказку о том, как это сделать правильно? Благодаря

ответ

4

Вы действительно только один вызов zip от решения:

movies = [ 
    {:movie=>"The Neon Demon", :time=>"4:15 PM"}, 
    {:movie=>"Breaking a Monster", :time=>"6:45 PM"}, 
    {:movie=>"The Citizen", :time=>"9:00 PM"} 
] 

dates = [ 
    'Tuesday 8, November', 
    'Wednesday 9, November', 
    'Thursday 10, November' 
] 

dates.zip(movies).to_h 
# => {"Tuesday 8, November"=>{:movie=>"The Neon Demon", :time=>"4:15 PM"}, ... } 

В качестве даты примечание форматы, такие как, что очень раздражает с точки зрения программирования, так как они не могут быть отсортированы и язык конкретный. Формат даты ISO, такой как 2016-11-08, на практике работает намного лучше.

+1

Чтобы добавить к вопросу о форматировании, вы также можете использовать 'l' для форматирования даты ISO, чтобы он выглядел как формат времени вашего языкового стандарта. –

+0

Спасибо. zip выглядит очень удобно. Я попробую и вернусь к вам. – matski

+0

Это супер-просто и, безусловно, работает, но результат не такой, как я ожидал. В день должно быть больше одного фильма, но я, очевидно, не сделал этого ясно в своем вопросе, поэтому я все равно отмечаю это как решение. Благодаря! Спасибо также @Eli Sadoff за точку в форматировании даты. – matski

1

Существует редко только один способ сделать расчет в Ruby, и это не исключение. Вот еще один способ. Обратите внимание, что я разрешил возможность просмотра двух или более фильмов в тот же день.

Код

def aggregate_by_date(dates, movies) 
    movies.each_index.with_object({}) { |i,h| (h[dates[i]] ||= []) << movies[i] } 
end 

Пример

movies = [ 
    {:movie=>"The Neon Demon", :time=>"4:15 PM"}, 
    {:movie=>"Breaking a Monster", :time=>"6:45 PM"}, 
    {:movie=>"The Citizen", :time=>"9:00 PM"}, 
    {:movie=>"Citizen Kane", :time=>"9:40 PM"} 
] 

dates = [ 
    'Tuesday 8, November', 
    'Wednesday 9, November', 
    'Thursday 10, November', 
    'Wednesday 9, November' 
] 

h = aggregate_by_date(dates, movies) 
    #=> {"Tuesday 8, November"=>[{:movie=>"The Neon Demon", :time=>"4:15 PM"}], 
    # "Wednesday 9, November"=>[{:movie=>"Breaking a Monster", :time=>"6:45 PM"}, 
    #        {:movie=>"Citizen Kane", :time=>"9:40 PM"}], 
    # "Thursday 10, November"=>[{:movie=>"The Citizen", :time=>"9:00 PM"}]} 

Объяснение

Для i = 0 вычислим:

(h[dates[i]] ||= []) << movies[i] 
    #=> (h[dates[i] = h[dates[i] || []) << movies[i] 
    #=> (h[dates[i] = nil || []) << movies[i] 
    #=> (h[dates[i] = []) << movies[i] 
    #=> h["Tuesday 8, November"]=>[{:movie=>"The Neon Demon", :time=>"4:15 PM"}] 

h #=> {"Tuesday 8, November"=>[{:movie=>"The Neon Demon", :time=>"4:15 PM"}]} 

Для i = 1 расчет аналогичен:

(h[dates[i]] ||= []) << movies[i] 
    #=> "Wednesday 9, November"=>[{:movie=>"Breaking a Monster", :time=>"6:45 PM"}] 
h #=> {"Tuesday 8, November"=>[{:movie=>"The Neon Demon", :time=>"4:15 PM"}], 
#  "Wednesday 9, November"=>[{:movie=>"Breaking a Monster", :time=>"6:45 PM"}]} 

Для i = 2, еще раз, расчет аналогичен:

(h[dates[i]] ||= []) << movies[i] 
    #=> "Thursday 10, November"=>[{:movie=>"The Citizen", :time=>"9:00 PM"}] 
h #=> {"Tuesday 8, November"=>[{:movie=>"The Neon Demon", :time=>"4:15 PM"}], 
    # "Wednesday 9, November"=>[{:movie=>"Breaking a Monster", :time=>"6:45 PM"}], 
    # "Thursday 10, November"=>[{:movie=>"The Citizen", :time=>"9:00 PM"}]} 

Но i = 3, все меняется, так как h теперь ключ 'Wednesday 9, November', поэтому расчет проще:

(h[dates[i]] ||= []) << movies[i] 
    #=> (h[dates[i] = h[dates[i] || []) << movies[i] 
    #=> (h[dates[i] = 
    #  [{:movie=>"Breaking a Monster", :time=>"6:45 PM"}] || []) << movies[i] 
    #=> h[dates[i] = 
     [{:movie=>"Breaking a Monster", :time=>"6:45 PM"}] << movies[i] 
    #=> h['Wednesday 9, November'] = 
    #=> [{:movie=>"Breaking a Monster", :time=>"6:45 PM"}, 
    #  {:movie=>"Citizen Kane", :time=>"9:40 PM"}] 

h, который теперь возвращается из блока, равен хешу, представленному в примере.

Альтернативный метод

Вот еще один способ это может быть написано, что я представляю без объяснения причин.

def aggregate_by_date(dates, movies) 
    dates.zip(movies).group_by(&:first). 
    tap { |h| h.keys.each { |k| h[k] = h[k].map(&:last) } } 
end 
+1

Спасибо за это. Не могли бы вы немного объяснить, что здесь происходит. Я получаю суть, но, к примеру, с_объектом. ({}) Полностью потерян на мне! – matski

+2

Использование [Enumerator # with_object] (http://ruby-doc.org/core-2.3.0/Enumerator.html#method-i-with_object) (объект является изначально пустым хэшем, на который ссылается блочная переменная ' h', который возвращается блоком) - это всего лишь способ записать следующее в одной строке, а не в три: 'h = {}; movies.each_index {| i | h [даты [i]] = фильмы [i]}; h'. См. Также [Array # each_index] (http://ruby-doc.org/core-2.3.0/Array.html#method-i-each_index). Другие вопросы? –

1

Другие ответы являются хорошими. Вот решение, которое даст ожидаемый результат.

movies = [ 
    {:movie=>"The Neon Demon", :time=>"4:15 PM"}, 
    {:movie=>"Breaking a Monster", :time=>"6:45 PM"}, 
    {:movie=>"The Citizen", :time=>"9:00 PM"} 
] 

dates = [ 
    'Tuesday 8, November', 
    'Tuesday 8, November', 
    'Thursday 10, November' 
] 

# Add date to movies array so that we can do group_by in next step 
movies_array_with_date = movies.each.with_index{ |m, i| m[:date] = dates[i] } 

# Group by the array elements by date 
movies_grouped_by_date = movies_array_with_date.group_by {|e| e[:date]} 

# Now get rid of date from values of hash as it is not desired in final output 
desired_hash = movies_grouped_by_date.each {|k,v| v.each {|a| a.delete(:date)}} 

#=> {"Tuesday 8, November"=> 
#  [{:movie=>"The Neon Demon", :time=>"4:15 PM"}, 
#  {:movie=>"Breaking a Monster", :time=>"6:45 PM"}], 
# "Thursday 10, November"=> 
#  [{:movie=>"The Citizen", :time=>"9:00 PM"}]} 


desired_hash = movies_grouped_by_date.each {|k,v| v.each {|a| a.delete(:date)}} 

puts JSON.pretty_generate desired_hash 

#=> { 
#  "Tuesday 8, November": [ 
#   { 
#   "movie": "The Neon Demon", 
#   "time": "4:15 PM" 
#   }, 
#   { 
#   "movie": "Breaking a Monster", 
#   "time": "6:45 PM" 
#   } 
#  ], 
#  "Thursday 10, November": [ 
#   { 
#   "movie": "The Citizen", 
#   "time": "9:00 PM" 
#   } 
#  ] 
#  } 
Смежные вопросы