在迭代另一个数组时,遍历数组的更好方法是什么?例如,如果我有两个如下所示的数组:
names = [ "Rover", "Fido", "Lassie", "Calypso"]
breeds = [ "Terrier", "Lhasa Apso", "Collie", "Bulldog"]
假设阵列彼此对应 - 也就是说,Rover是一个Terrier,Fido是Lhasa Apso等等 - 我想创建一个狗类,并为每个项目创建一个新的狗对象:
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
但是,我觉得使用索引变量是错误的方法。什么是更好的方式?
答案 0 :(得分:24)
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 mentioned一样,#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'] ]
答案 1 :(得分:3)
each_with_index跳跃于脑海,这是一种更好的方式来做到这一点。虽然有一个更好的整体答案,但这种情况就是拉链的用途。
答案 2 :(得分:3)
改编自Flanagan和Matz,“Ruby Programming Language”,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">
我认为是我们想要的!
答案 3 :(得分:2)
除了each_with_index
(Matt提到),还有each_index
。我有时会使用它,因为它使程序更加对称,因此错误的代码将look wrong。
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