Что такое «Рубиновый способ» для перебора двух массивов одновременно - PullRequest
119 голосов
/ 27 августа 2010

Больше синтаксического любопытства, чем проблемы, которую нужно решить ...

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

@budget = [ 100, 150, 25, 105 ]
@actual = [ 120, 100, 50, 100 ]

Я знаю, что могу использовать each_index и индексировать в массивы следующим образом:

@budget.each_index do |i|
  puts @budget[i]
  puts @actual[i]
end

Есть ли Ruby способ длясделать это лучше?Что-то как это?

# Obviously doesn't achieve what I want it to - but is there something like this?
[@budget, @actual].each do |budget, actual|
  puts budget
  puts actual
end

Ответы [ 7 ]

260 голосов
/ 27 августа 2010
>> @budget = [ 100, 150, 25, 105 ]
=> [100, 150, 25, 105]
>> @actual = [ 120, 100, 50, 100 ]
=> [120, 100, 50, 100]

>> @budget.zip @actual
=> [[100, 120], [150, 100], [25, 50], [105, 100]]

>> @budget.zip(@actual).each do |budget, actual|
?>   puts budget
>>   puts actual
>> end
100
120
150
100
25
50
105
100
=> [[100, 120], [150, 100], [25, 50], [105, 100]]
21 голосов
/ 27 августа 2010

Используйте метод Array.zip и передайте ему блок для последовательного перебора соответствующих элементов.

20 голосов
/ 22 октября 2014

Существует еще один способ перебора двух массивов одновременно с использованием перечислителей:

2.1.2 :003 > enum = [1,2,4].each
 => #<Enumerator: [1, 2, 4]:each> 
2.1.2 :004 > enum2 = [5,6,7].each
 => #<Enumerator: [5, 6, 7]:each> 
2.1.2 :005 > loop do
2.1.2 :006 >     a1,a2=enum.next,enum2.next
2.1.2 :007?>   puts "array 1 #{a1} array 2 #{a2}"
2.1.2 :008?>   end
array 1 1 array 2 5
array 1 2 array 2 6
array 1 4 array 2 7

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

16 голосов
/ 13 января 2013

В дополнение к a.zip(b).each{|x,y| }, как уже говорили другие, вы также можете сказать [a,b].transpose.each{|x,y| }, что кажется мне чуть более симметричным. Возможно, не так быстро, так как вы создаете дополнительный массив [a,b].

14 голосов
/ 01 декабря 2014

Относительно исходного вопроса, для перебора массивов неравного длины, где вы хотите, чтобы значения циклически повторялись, вы можете использовать

[1,2,3,4,5,6].zip([7,8,9].cycle)

, а Ruby даст вам

[[1, 7], [2, 8], [3, 9], [4, 7], [5, 8], [6, 9]]

Это спасет вас от значений nil, которые вы получите, просто используя zip

6 голосов
/ 22 июля 2015

Простое сжатие двух массивов работает хорошо, если вы имеете дело с массивами.Но что, если вы имеете дело с бесконечными перечислителями, например, такими как:

enum1 = (1..5).cycle
enum2 = (10..12).cycle

enum1.zip(enum2) не удается, потому что zip пытается оценить все элементы и объединить их.Вместо этого сделайте следующее:

enum1.lazy.zip(enum2)

Этот lazy спасет вас, сделав полученный счетчик ленивым-оценивать.

2 голосов
/ 26 июня 2017

Как насчет компромисса и использования #each_with_index?

include Enumerable 

@budget = [ 100, 150, 25, 105 ]
@actual = [ 120, 100, 50, 100 ]

@budget.each_with_index { |val, i| puts val; puts @actual[i] }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...