2008-11-22 21 views
280

PHP, por todas sus verrugas, es bastante bueno en este aspecto. No hay diferencia entre una matriz y un hash (tal vez soy ingenuo, pero esto parece obvio derecho a mí), y para recorrer ya sea que acaba de hacer¿Cuál es la forma "correcta" de iterar a través de una matriz en Ruby?

foreach (array/hash as $key => $value) 

En Rubí hay un montón de maneras de hacer este tipo de cosas:

array.length.times do |i| 
end 

array.each 

array.each_index 

for i in array 

hashes tienen más sentido, ya que acabo siempre uso

hash.each do |key, value| 

por qué no puedo hacer esto para las matrices? Si quiero recordar solo un método, creo que puedo usar each_index (ya que hace que tanto el índice como el valor estén disponibles), pero es molesto tener que hacer array[index] en lugar de solo value.


Oh, se me olvidó array.each_with_index. Sin embargo, este es una mierda porque va |value, key| y hash.each va |key, value|! ¿No es esto una locura?

+0

supongo array '' # each_with_index' utiliza | valor, clave | 'debido a que el nombre del método implica el orden, mientras que el orden utilizado para' almohadilla # each' imita la sintaxis 'hash [key] = value'? – benjineer

+3

Si recién está comenzando con bucles en Ruby, luego revise [usando seleccionar, rechazar, recopilar, inyectar y detectar] (http: // matthewcarriere.com/2008/06/23/using-select-reject-collect-inject-and-detect /) –

Respuesta

470

Esto iterar a través de todos los elementos:

array = [1, 2, 3, 4, 5, 6] 
array.each { |x| puts x } 

Prints:

1 
2 
3 
4 
5 
6 

Esto iterar a través de todos los elementos que le da el valor y el índice:

array = ["A", "B", "C"] 
array.each_with_index {|val, index| puts "#{val} => #{index}" } 

Impresiones:

A => 0 
B => 1 
C => 2 

No estoy muy seguro de su pregunta cuál está buscando.

+1

Fuera de tema No puedo encontrar cada_con_index en ruby ​​1.9 en la matriz – CantGetANick

+15

@CantGetANick: la clase Array incluye el módulo Enumerable que tiene esto método. –

14

Use each_with_index cuando necesite ambos.

ary.each_with_index { |val, idx| # ... 
62

creo que no hay forma de una derecha. Hay muchas formas diferentes de iterar, y cada una tiene su propio nicho.

  • each es suficiente para muchos usos, ya que a menudo no se preocupan por los índices.
  • each_ with _index actúa como Hash # cada uno - obtienes el valor y el índice.
  • each_index - solo los índices. No uso este a menudo. Equivalente a "longitud.tiempo".
  • map es otra forma de iterar, útil cuando desea transformar una matriz en otra.
  • select es el iterador que se utiliza cuando se desea elegir un subconjunto.
  • inject es útil para generar sumas o productos, o recopilar un solo resultado.

Puede parecer mucho para recordar, pero no se preocupe, puede vivir sin conocerlas todas.Pero a medida que comienzas a aprender y usar los diferentes métodos, tu código se volverá más claro y más claro, y estarás en camino hacia el dominio de Ruby.

+2

Me gusta esta lista concisa pero completa. Gracias ! – gfd

+3

A veces realmente deseo que podamos * respuestas * favoritas también ... –

7

Tratando de hacer lo mismo constantemente con arreglos y hashes podría ser sólo un olor código, pero, a riesgo de mi ser catalogados como un medio-mono-parche codorous, si usted está en busca de un comportamiento coherente , ¿esto hace el truco ?:

class Hash 
    def each_pairwise 
     self.each { | x, y | 
      yield [x, y] 
     } 
    end 
end 

class Array 
    def each_pairwise 
     self.each_with_index { | x, y | 
      yield [y, x] 
     } 
    end 
end 

["a","b","c"].each_pairwise { |x,y| 
    puts "#{x} => #{y}" 
} 

{"a" => "Aardvark","b" => "Bogle","c" => "Catastrophe"}.each_pairwise { |x,y| 
    puts "#{x} => #{y}" 
} 
12

las otras respuestas son muy bien, pero yo quería señalar una cosa más periférica: las matrices son ordenadas, mientras que los hashes no están en 1.8. (En Ruby 1.9, los hashes se ordenan por orden de inserción de claves). Por lo tanto, antes de 1.9 no tendría sentido iterar sobre un hash de la misma manera o secuencia que los arrays, que siempre han tenido un orden definido. No sé cuál es el orden predeterminado para las matrices asociativas de PHP (aparentemente, mi google fu no es lo suficientemente fuerte como para descubrirlo), pero no sé cómo se pueden considerar las matrices PHP y las matrices asociativas de PHP. ser "lo mismo" en este contexto, ya que el orden de las matrices asociativas parece indefinido.

Como tal, el modo Ruby parece más claro e intuitivo para mí. :)

+1

¡los hashes y las matrices son la misma cosa! las matrices asignan números enteros a objetos y hash asignan objetos a objetos. las matrices son solo casos especiales de hashes, ¿no? –

+1

Como dije, una matriz es un conjunto ordenado. Un mapeo (en el sentido genérico) no está ordenado. Si restringe la clave establecida a enteros (como con las matrices), sucede que la clave establecida tiene un orden. En un mapeo genérico (Hash/Matriz Asociativa), las claves pueden no tener un orden. – Pistos

+0

@Horace: hashes y matrices no son lo mismo. Si uno es una acase especial del otro, no pueden ser lo mismo. Pero lo que es peor, las matrices no son un tipo especial de hash, es solo una forma abstracta de verlos. Como señaló Brent anteriormente, usar hashes y matrices indistintamente podría indicar un olor a código. – Zane

3

Si usa el enumerable mixin (como hace Rails) puede hacer algo similar al fragmento de php que se muestra. Solo usa el método each_slice y aplana el hash.

require 'enumerator' 

['a',1,'b',2].to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" } 

# is equivalent to... 

{'a'=>1,'b'=>2}.to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" } 

Requiere menos parche de mono.

Sin embargo, esto causa problemas cuando tiene una matriz recursiva o un hash con valores de matriz. En ruby ​​1.9 este problema se resuelve con un parámetro para el método de aplanar que especifica qué tan profundo recurrir.

# Ruby 1.8 
[1,2,[1,2,3]].flatten 
=> [1,2,1,2,3] 

# Ruby 1.9 
[1,2,[1,2,3]].flatten(0) 
=> [1,2,[1,2,3]] 

En cuanto a la pregunta de si esto es un olor a código, no estoy seguro. Usualmente, cuando tengo que doblarme hacia atrás para iterar sobre algo, retrocedo y me doy cuenta de que estoy atacando el problema.

52

No estoy diciendo que Array ->|value,index| y Hash ->|key,value| no es una locura (ver el comentario de Horacio Loeb), pero estoy diciendo que hay una manera sana para esperar que este arreglo.

Cuando me ocupo de matrices, estoy centrado en los elementos de la matriz (no en el índice porque el índice es transitorio). El método tiene cada uno índice, es decir, cada índice +, o | cada, índice | o |value,index|. Esto también es consistente con que el índice se vea como un argumento opcional, p. | valor | es equivalente a | valor, índice = nil | que es consistente con | valor, índice |.

Cuando estoy tratando con hashes, estoy a menudo más centrado en las claves que los valores, y por lo general estoy tratando con claves y valores en ese orden, ya sea key => value o hash[key] = value.

Si desea escribir patos, utilice explícitamente un método definido como lo mostró Brent Longborough, o un método implícito como lo mostró maxhawkins.

Ruby se trata de acomodar el idioma para adaptarse al programador, no de que el programador se adapte al idioma. Es por eso que hay muchas maneras. Hay muchas maneras de pensar sobre algo. En Ruby, eliges el más cercano y el resto del código generalmente cae de forma muy clara y concisa.

En cuanto a la pregunta original, "¿Cuál es la forma" correcta "de iterar a través de una matriz en Ruby?", Bueno, creo que la forma básica (es decir, sin poder de azúcar sintáctico o poder orientado a objetos) es hacer:

for index in 0 ... array.size 
    puts "array[#{index}] = #{array[index].inspect}" 
end 

pero Ruby es todo sobre el azúcar sintáctica de gran alcance y el poder orientado a objetos, pero de todos modos aquí es el equivalente para los hashes, y las teclas se pueden pedir o no:

for key in hash.keys.sort 
    puts "hash[#{key.inspect}] = #{hash[key].inspect}" 
end 

lo tanto, mi respuesta es , "La forma" correcta "de iterar a través de una matriz en Ruby depende de ti (es decir, el programador o el equipo de programación) y el proyecto ". El mejor programador de Ruby hace la mejor elección (de qué potencia sintáctica y qué enfoque orientado a objetos). El mejor programador de Ruby continúa buscando más formas.


Ahora, quiero hacer otra pregunta: "¿Qué es la‘correcta’para iterar a través de un rango en Ruby hacia atrás?"! (Esta pregunta es cómo llegué a esta página.)

Es bueno hacer (para los delanteros):

(1..10).each{|i| puts "i=#{i}" } 

pero no me gusta hacer (por el revés):

Bueno, realmente no me importa hacer eso demasiado, pero cuando estoy enseñando retroceder, quiero mostrar a mis alumnos una buena simetría (es decir, con una diferencia mínima, por ejemplo, solo agregar un reverso, o un paso -1, pero sin modificar nada más). Usted puede hacer (por simetría):

(a=*1..10).each{|i| puts "i=#{i}" } 

y

(a=*1..10).reverse.each{|i| puts "i=#{i}" } 

la que no me gusta mucho, pero no se puede hacer

(*1..10).each{|i| puts "i=#{i}" } 
(*1..10).reverse.each{|i| puts "i=#{i}" } 
# 
(1..10).step(1){|i| puts "i=#{i}" } 
(1..10).step(-1){|i| puts "i=#{i}" } 
# 
(1..10).each{|i| puts "i=#{i}" } 
(10..1).each{|i| puts "i=#{i}" } # I don't want this though. It's dangerous 

Se podría en última instancia, hacer

class Range 

    def each_reverse(&block) 
    self.to_a.reverse.each(&block) 
    end 

end 

pero Quiero enseñar enfoques puros de Ruby en lugar de enfoques orientados a objetos (por el momento). Me gustaría repetir al revés:

  • sin crear una matriz (considerar 0..1000000000)
  • de trabajo para cualquier rango (por ejemplo, cadenas, no sólo enteros)
  • sin necesidad de utilizar ningún poder orientada a objetos extra (es decir, no hay modificación de clase)

Creo que esto es imposible sin definir un método pred, lo que significa que se debe modificar la clase Range para usarlo. Si puede hacerlo, hágamelo saber, de lo contrario, la confirmación de la imposibilidad sería apreciada, aunque sería decepcionante. Quizás Ruby 1.9 aborda esto.

(Gracias por su tiempo en leer esto.)

+4

'1.upto (10) do | i | puts i end' y '10.downto (1) do puts i end' tipo de da la simetría que quería. Espero que ayude. No estoy seguro si funcionaría para cadenas y tal. – Suren

+0

(1..10) .to_a.sort {| x, y | y <=> x} .each {| i | pone "i = # {i}"} - el reverso es más lento. – Davidslv

+0

Puede '[* 1..10]. Cada uno {| i | pone "i = # {i}"} '. – Hauleth

5

que había estado tratando de construir un menú (en el camping y Markaby) usando un hash.

Cada artículo tiene 2 elementos: una etiqueta menú y una URL, por lo que un hash parecía correcto, pero la URL '/' para 'Inicio' siempre apareció el pasado (como era de esperar de un hash) , por lo que los elementos del menú aparecieron en el orden incorrecto.

Usando una matriz con each_slice hace el trabajo:

['Home', '/', 'Page two', 'two', 'Test', 'test'].each_slice(2) do|label,link| 
    li {a label, :href => link} 
end 

Adición de valores extra para cada elemento del menú (por ejemplo, como un CSS ID Nombre) sólo significa aumentar el valor de la rebanada. Por lo tanto, como un hash pero con grupos que constan de cualquier cantidad de elementos. Perfecto.

Así que esto es solo para agradecer por haber sugerido inadvertidamente una solución.

Obvio, pero vale la pena indicar: Sugiero comprobar si la longitud de la matriz es divisible por el valor de corte.

2

La forma correcta es aquella con la que se sienta más cómodo y que haga lo que quiera que haga. En la programación rara vez hay una forma "correcta" de hacer las cosas, más a menudo hay múltiples formas de elegir.

Si se siente cómodo con ciertas formas de hacer las cosas, hágalo, a menos que no funcione, entonces es hora de encontrar una mejor manera.

1

En Ruby 2.1, se elimina el método each_with_index. su lugar se puede utilizar each_index

Ejemplo:

a = [ "a", "b", "c" ] 
a.each_index {|x| print x, " -- " } 

produce:

0 -- 1 -- 2 -- 
+4

Esto no es verdad. Como se afirma en los comentarios de la respuesta aceptada, el motivo por el que 'each_with_index' no aparece en la documentación es porque lo proporciona el módulo' Enumerable'. – ihaztehcodez

4

Estas son las cuatro opciones que aparecen en su pregunta, organizado por la libertad de control. Es posible que desee utilizar uno diferente según lo que necesite.

  1. Sólo tienes que ir a través de valores:

    array.each 
    
  2. sólo tiene que ir a través de índices:

    array.each_index 
    
  3. Ir a través de los índices + variable de índice:

    for i in array 
    
  4. Co ntrol cuenta de bucles + índice de variables:

    array.length.times do | i | 
    
Cuestiones relacionadas