[英]Basic Array Iteration in Ruby
在遍歷另一個數組時遍歷數組的更好方法是什么? 例如,如果我有兩個如下數組:
names = [ "Rover", "Fido", "Lassie", "Calypso"]
breeds = [ "Terrier", "Lhasa Apso", "Collie", "Bulldog"]
假設數組相互對應-即Rover是Terrier,Fido是Lhasa Apso,等等。-我想創建一個dog類,並為每個項創建一個新的dog對象:
class Dog
attr_reader :name, :breed
def initialize(name, breed)
@name = name
@breed = breed
end
end
我可以通過以下方式遍歷名稱和品種:
index = 0
names.each do |name|
Dog.new("#{name}", "#{breeds[index]}")
index = index.next
end
但是,我感到使用index變量是錯誤的處理方式。 有什么更好的方法?
dogs = names.zip(breeds).map { |name, breed| Dog.new(name, breed) }
Array#zip
將目標數組與參數元素交織在一起,因此
irb> [1, 2, 3].zip(['a', 'b', 'c'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
您可以使用不同長度的數組(在這種情況下,目標數組確定結果數組的長度,多余的條目用nil
填充)。
irb> [1, 2, 3, 4, 5].zip(['a', 'b', 'c'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'], [4, nil], [5, nil] ]
irb> [1, 2, 3].zip(['a', 'b', 'c', 'd', 'e'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
您還可以將兩個以上的數組壓縮在一起:
irb> [1,2,3].zip(['a', 'b', 'c'], [:alpha, :beta, :gamma])
#=> [ [1, 'a', :alpha], [2, 'b', :beta], [3, 'c', :gamma] ]
Array#map
是一種轉換數組的好方法,因為它返回一個數組,其中每個條目都是在目標數組中相應條目上運行塊的結果。
irb> [1,2,3].map { |n| 10 - n }
#=> [ 9, 8, 7 ]
在數組數組上使用迭代器時,如果您提供了多個參數塊,則數組條目將自動分解為這些參數:
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each { |array| p array }
[ 1, 'a' ]
[ 2, 'b' ]
[ 3, 'c' ]
#=> nil
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each do |num, char|
...> puts "number: #{num}, character: #{char}"
...> end
number 1, character: a
number 2, character: b
number 3, character: c
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
就像Matt Briggs 提到的那樣 , #each_with_index
是另一個#each_with_index
了解的好工具。 它遍歷數組的元素,依次將每個元素傳遞給一個塊。
irb> ['a', 'b', 'c'].each_with_index do |char, index|
...> puts "character #{char} at index #{index}"
...> end
character a at index 0
character b at index 1
character c at index 2
#=> [ 'a', 'b', 'c' ]
當使用像#each_with_index
這樣的迭代器時,可以使用括號將數組元素分解為它們的組成部分:
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each_with_index do |(num, char), index|
...> puts "number: #{num}, character: #{char} at index #{index}"
...> end
number 1, character: a at index 0
number 2, character: b at index 1
number 3, character: c at index 2
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
想到了each_with_index ,這是您執行此操作的更好方法。 但是,rampion的總體答案更好,這種情況就是zip的目的。
改編自Flanagan和Matz的“ Ruby編程語言”,第5.3.5節“外部迭代器”,示例5-1,p。 139:
++++++++++++++++++++++++++++++++++++++++++++
require 'enumerator' # needed for Ruby 1.8
names = ["Rover", "Fido", "Lassie", "Calypso"]
breeds = ["Terrier", "Lhasa Apso", "Collie", "Bulldog"]
class Dog
attr_reader :name, :breed
def initialize(name, breed)
@name = name
@breed = breed
end
end
def bundle(*enumerables)
enumerators = enumerables.map {|e| e.to_enum}
loop {yield enumerators.map {|e| e.next} }
end
bundle(names, breeds) {|x| p Dog.new(*x) }
+++++++++++++++++++++++++++++++++++++++++++++
輸出:
#<Dog:0x10014b648 @name="Rover", @breed="Terrier">
#<Dog:0x10014b0d0 @name="Fido", @breed="Lhasa Apso">
#<Dog:0x10014ab80 @name="Lassie", @breed="Collie">
#<Dog:0x10014a770 @name="Calypso", @breed="Bulldog">
我想這就是我們想要的!
和each_with_index
(由Matt提到)一樣,還有each_index
。 我有時會使用它,因為它使程序更對稱,因此錯誤的代碼將看起來是錯誤的 。
names.each_index do |i|
name, breed = dogs[i], breeds[i] #Can also use dogs.fetch(i) if you want to fail fast
Dog.new(name, breed)
end
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.