繁体   English   中英

Ruby中的基本数组迭代

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM