Який "спосіб Ruby" переглядати два масиви одночасно


127

Більше синтаксичної цікавості, ніж проблеми для вирішення ...

У мене є два масиви однакової довжини і хочу повторити їх обидва одночасно - наприклад, вивести обидва їх значення за певним індексом.

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

Я знаю, що можу використовувати each_indexта індексувати масиви так:

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

Чи існує спосіб Рубі зробити це краще? Щось подібне ?

# 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

1
чи обидва масиви однакових розмірів?
Анураг

1
Так - обидва, як відомо, однакової довжини
nfm

Відповіді:


276
>> @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]]

12
".each" може розгортати елементи масиву? Цікаво, скільки ще Рубі я не знаю: /
Микита Рибак

5
Якщо ви збираєтесь використовувати такий вираз, завжди краще використовувати дужки для викликів методів, про всяк випадок. @ budget.zip (@actual) .each
AboutRuby

1
@ AboutRuby: У цьому випадку це потрібно! Виправлено.
Марк-Андре Лафортун

2
коли я бачу такі рубінові лінії, я кидаю руки в повітря і обходжу кімнату, як шампион!
iGbanam

9
Це масштаб? Якщо у мене є 2 10000 масивів елементів, це вимагає створення масиву 20000 елементів? Документи пропонують це.
Том Андерсен

21

Використовуйте Array.zipметод і передайте йому блок, щоб послідовно повторювати відповідні елементи.


20

Існує ще один спосіб перебрати два масиви одночасно за допомогою обчислювачів:

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

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


1
Чи є спосіб отримати індекс всередині loopпоказаного вище?
Біжу

16

Окрім того, a.zip(b).each{|x,y| }як сказали інші, ви також можете сказати [a,b].transpose.each{|x,y| }, що вражає мене як крихітного трохи симетричнішого. Мабуть, не так швидко, оскільки ви створюєте додатковий [a,b]масив.


11
+1 Одним із приємних речей transposeє те, що він створює виняток, якщо два масиви не однакової довжини.
Ендрю Грімм

14

Пов’язане з оригінальним запитанням, для ітерації над масивами неоднакової довжини, де ви хочете, щоб значення оберталися навколо, ви можете використовувати

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

і Рубі дасть тобі

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

Це позбавить вас від nilзначень, які ви отримаєте від просто використання zip


6

Якщо ви маєте справу з масивами, просто поєднувати два масиви разом добре. Але що робити, якщо ви маєте справу з нескінченними нумераторами, такими як:

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

enum1.zip(enum2)не вдається, тому що zipнамагається оцінити всі елементи та поєднати їх. Натомість зробіть це:

enum1.lazy.zip(enum2)

Це lazyрятує вас, зробивши отриманий перелік ледачим оцінкою.


2

Як щодо компромісу та використання #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] }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.