2016-01-05 1 views
2

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

Входной массив хэшей выглядит следующим образом:
[{label: "first", data: [1, 2]}, {label: "second", data: [3, 4, 5]}, {label: "third", data: []}, {label: "fourth", data: [6]}]
В этом примере max_returns бы что-то высокое, как: 10

def round_robin(arr, max_returns) 
    result = '' 
    i = 0 # number of grabbed elements 
    j = 0 # inner array position 
    k = 0 # outer array position 
    l = 0 # number of times inner array length has been exceeded 
    while i < max_returns do 
    if k >= arr.length 
     j += 1 
     k = 0 
    end 
    element = arr[k] 
    if element[:data].empty? 
     k += 1 
     next 
    end 

    if j >= element[:data].length 
     l += 1 
     k += 1 

     if l > arr.length && i < max_returns 
     break 
     end 
     next 
    end 
    result += element[:label] + ': ' + element[:data][j].to_s + ', ' 
    i += 1 
    k += 1 
    end 
    result 
end 

Основываясь на входе приведенного выше, вывод должен быть:
"first: 1, second: 3, fourth: 6, first: 2, second: 4, second: 5"

Также: max_returns - максимальное количество полученных результатов. Так что, если мой пример был max_returns = 3, то вывод был бы:
"first: 1, second: 3, fourth: 6"

Вопрос: Есть ли лучше, или более эффективный способ, чтобы захватить данные из нескольких массивов в циклическому?

+0

Shoun'd, что будет 'второй: 3' в первую очередь? '{label:" second ", data: [" 3 "," 4 "," 5 "]}' – Draco18s

+0

Вы правы, спасибо, что нашли это! Я пересмотрел свой пример, чтобы соответствовать. – Scytherswings

+0

'round-robin' не является допустимым именем метода и кажется, что существует' TypeError' при добавлении 'element [: data] [j]' в строку 'result' – Stefan

ответ

3

Это будет работать:

arr = [{ label: "first", data: [1, 3] }, 
     { label: "second", data: [3, 4, 5] }, 
     { label: "third", data: [] }, 
     { label: "fourth", data: [6] }] 

results = [] 
arr.each do |h| 
    h[:data].each_with_index do |d, i| 
    results[i] ||= [] 
    results[i] << "#{h[:label]}: #{d}" 
    end 
end 

results.flatten.join(', ') 
#=> "first: 1, second: 3, fourth: 6, first: 3, second: 4, second: 5" 
+1

Лучший ответ до сих пор, imo. Читатели: Стефан мог бы написать это как один лайнер, но решил не делать этого, по-видимому, чтобы было легче следовать. Сжатой формой будет: 'arr.each_with_object ([]) {| h, results | h [: data] .each_with_index {| d, i | (результаты [i] || = []) << "# {h [: label]}: # {d}"}} .flatten.join (',') '. Стефан, я полагаю, у вас есть хороший повод для отсутствия в праздники. –

1

Я не уверен, что круговой, но вот решение, которое дает выход вам нужно:

Версия на основе первоначального удаления элементов массива:

arr = [{label: "first", data: [1, 2]}, {label: "second", data: [3, 4, 5]}, {label: "third", data: []}, {label: "fourth", data: [6]}] 

loop do 
    arr.each do |hash|        # go through each hash 
    num = hash[:data].delete_at(0)     # remove first element in data array 
    puts "#{hash[:label]}: #{num}" unless num.nil? # output it if it's not nil(means array was empty) 
    end 
    break if arr.map { |i| i[:data] }.flatten == [] # stop if all arrays are empty 
end 

версии, которая не делает изменения начальный массив:

arr = [{label: "first", data: [1, 2]}, {label: "second", data: [3, 4, 5]}, {label: "third", data: []}, {label: "fourth", data: [6]}] 

max_data_size = arr.map { |i| i[:data] }.map(&:size).max 
loop.with_index do |_, i| 
    arr.each do |hash| 
    num = hash[:data][i] 
    puts "#{hash[:label]}: #{num}" unless num.nil? 
    end 
    break if i >= max_data_size - 1 
end 
+0

Я использовал вашу вторую версию в тестах. Когда я изменил данные, чтобы лучше подойти к проблеме, ваше решение прошло немного медленнее, чем при первом запуске тестов, поэтому я изменил ответ на @ stefan's. Спасибо за ваш ответ, хотя! – Scytherswings

3
▶ input = [{label: "first", data: [1, 2]}, 
      {label: "second", data: [3, 4, 5]}, 
      {label: "third", data: []}, 
      {label: "fourth", data: [6]}] 

▶ max = input.max_by { |e| e[:data].size }[:data].size 

▶ input.map do |h| 
    [[h[:label]] * max].flatten.zip h[:data] # make it N×M (for transpose) 
    end.transpose.map do |e| 
    e.reject { |_, v| v.nil? }    # remove nils 
    end.flatten(1).map { |e| e.join ': ' }.join ', ' 

#⇒ "first: 1, second: 3, fourth: 6, first: 2, second: 4, second: 5" 

Без двух последних присоединяется результат будет массив о F массивы:

#⇒ [["first", 1], ["second", 3], ["fourth", 6], 
# ["first", 2], ["second", 4], ["second", 5]] 
2
arr = [{ label: "first", data: [1, 2] }, 
     { label: "second", data: [3, 4, 5] }, 
     { label: "third", data: [] }, 
     { label: "fourth", data: [6] }] 

labels, data = arr.map { |h| [h[:label], h[:data].dup] }.transpose 
    #=> [["first", "second", "third", "fourth"], [[1, 2], [3, 4, 5], [], [6]]] 
data.map(&:size).max.times.with_object([]) do |_,arr| 
    labels.each_index do |i| 
    d = data[i].shift 
    arr << "#{labels[i]}: #{d}" if d 
    end 
end.join(', ') 
    #=> "first: 1, second: 3, fourth: 6, first: 2, second: 4, second: 5" 
1

эталоны были все работают против одних и тех же данных. Я провел каждый ответ против четырех разных сценариев:
*_5 были запущены по сравнению с исходными данными: 852, 0, 0, 0
*_500 были запущены по тем же данным, но не более 500 возвратов.
*_2_5 были выполнены против данных в 4 массивах, размеры которых составляли: 656, 137, 0, 59, всего 852 записи.
*_2_500 были набегать arr2 с максимальной отдачей 500.

     user  system  total  real 
OP_5:    0.000000 0.000000 0.000000 ( 0.000120) 
Mudasobwa_5:  0.000000 0.000000 0.000000 ( 0.000108) 
Cary_5:   0.010000 0.000000 0.010000 ( 0.011316) 
Rustam_5:   0.000000 0.000000 0.000000 ( 0.000087) 
Wand_5:   0.010000 0.000000 0.010000 ( 0.003761) 
Stefan_5:   0.000000 0.000000 0.000000 ( 0.004007) 
OP_500:   0.010000 0.010000 0.020000 ( 0.017235) 
Mudasobwa_500:  0.010000 0.000000 0.010000 ( 0.006164) 
Cary_500:   0.010000 0.000000 0.010000 ( 0.011403) 
Rustam_500:  0.010000 0.000000 0.010000 ( 0.011884) 
Wand_500:   0.010000 0.000000 0.010000 ( 0.003743) 
Stefan_500:  0.000000 0.000000 0.000000 ( 0.002711) 
OP_2_5:   0.000000 0.000000 0.000000 ( 0.000052) 
Mudasobwa_2_5:  0.000000 0.000000 0.000000 ( 0.000140) 
Cary_2_5:   0.010000 0.000000 0.010000 ( 0.008196) 
Rustam_2_5:  0.000000 0.000000 0.000000 ( 0.000088) 
Wand_2_5:   0.000000 0.000000 0.000000 ( 0.003338) 
Stefan_2_5:  0.010000 0.000000 0.010000 ( 0.002597) 
OP_2_500:   0.000000 0.000000 0.000000 ( 0.002211) 
Mudasobwa_2_500: 0.000000 0.000000 0.000000 ( 0.006373) 
Cary_2_500:  0.010000 0.000000 0.010000 ( 0.008455) 
Rustam_2_500:  0.020000 0.000000 0.020000 ( 0.019453) 
Wand_2_500:  0.010000 0.000000 0.010000 ( 0.004846) 
Stefan_2_500:  0.000000 0.000000 0.000000 ( 0.003421) 
OP_avg:   0.002500 0.002500 0.005000 ( 0.004904) 
Mudasobwa_avg:  0.002500 0.000000 0.002500 ( 0.003196) 
Cary_avg:   0.010000 0.000000 0.010000 ( 0.009843) 
Rustam_avg:  0.007500 0.000000 0.007500 ( 0.007878) 
Wand_avg:   0.007500 0.000000 0.007500 ( 0.003922) 
Stefan_avg:  0.002500 0.000000 0.002500 ( 0.003184) 

В отличие от моего предыдущего теста, в среднем показывает, что ответ Стефана является на самом деле быстрее, победив ответ Mudasobwa путем 0,000012 секунд!

Примечание: Мне пришлось отредактировать некоторые ответы, чтобы подражать тому, что пыталось сделать мое первоначальное решение, поэтому в исходном коде есть дополнительные вещи, которые были добавлены специально.
Кроме того, некоторые решения не использовали предел max_returns (или не останавливались на пределе), из-за чего они занимали больше времени, чем другие (я обвиняю себя за менее чем звездное объяснение, когда я изначально задал вопрос).Я не принимал во внимание ограничение max_returns при выборе ответа, потому что единственными решениями, которые его соблюдали, были мои и Wand (см. Подробности для деталей).

Данные кода и примеры для выполнения этих тестов можно найти здесь: https://gist.github.com/scytherswings/65644610e20037bb948c

Спасибо всем за ответы!

+0

Спасибо за бенчмарк (хотя я и пришел последним). Когда вы сообщаете о бенчмарке, лучше всего включить контрольный код (включая данные или метод, который генерирует данные). Таким образом, читатели могут запустить тест после внесения изменений в свои методы. Какой из двух методов Рустама вы запустили? Чтобы сделать его сопоставимым с другими методами, вы должны использовать второй, который не мутирует массив. –

+0

Пожалуйста, добавьте решение Stefan к эталону. –

+0

@CarySwoveland Я обновил тесты с гораздо лучшими данными и ответом Стефана. Спасибо за поддержку, я многому научился из этого вопроса, и мне тоже было интересно это делать. Ура! – Scytherswings