loop - ruby select
¿Cuál es la manera “correcta” de iterar a través de una matriz en Ruby? (11)
Aquí están las cuatro opciones enumeradas en su pregunta, organizadas por la libertad de control. Es posible que desee utilizar uno diferente según lo que necesite.
Simplemente pasar a través de los valores:
array.each
Simplemente ir a través de los índices:
array.each_index
Ir a través de índices + variable de índice:
for i in array
Cuenta de bucle de control + variable de índice:
array.length.times do | i |
PHP, a pesar de todas sus verrugas, es bastante bueno en este sentido. No hay diferencia entre una matriz y un hash (tal vez sea ingenuo, pero esto obviamente me parece correcto), y para iterar a través de cualquiera de los dos simplemente
foreach (array/hash as $key => $value)
En Ruby hay muchas maneras de hacer este tipo de cosas:
array.length.times do |i|
end
array.each
array.each_index
for i in array
Los hash tienen más sentido, ya que siempre uso
hash.each do |key, value|
¿Por qué no puedo hacer esto para 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]
lugar de solo value
.
Oh cierto, me olvidé de array.each_with_index
. Sin embargo, este apesta porque va |value, key|
y hash.each
va |key, value|
! ¿No es esto una locura?
Creo que no hay un camino correcto . Hay muchas formas diferentes de iterar, y cada una tiene su propio nicho.
-
each
es suficiente para muchos usos, ya que a menudo no me importan los índices. -
each_ with _index
actúa como Hash # each: obtienes el valor y el índice. -
each_index
- sólo los índices. No uso este a menudo. Equivalente a "length.times". -
map
es otra forma de iterar, útil cuando se desea transformar una matriz en otra. -
select
es el iterador que se utilizará cuando desee elegir un subconjunto. -
inject
es útil para generar sumas o productos, o para recopilar un solo resultado.
Puede parecer mucho para recordar, pero no se preocupe, puede sobrevivir sin saberlo todo. Pero a medida que comiences a aprender y utilizar los diferentes métodos, tu código se volverá más y más claro, y estarás en tu camino hacia el dominio de Ruby.
En Ruby 2.1, se elimina cada método with_index. En su lugar puedes usar each_index
Ejemplo:
a = [ "a", "b", "c" ]
a.each_index {|x| print x, " -- " }
produce:
0 -- 1 -- 2 --
Esto recorrerá todos los elementos:
array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }
Huellas dactilares:
1
2
3
4
5
6
Esto recorrerá todos los elementos que le dan el valor y el índice:
array = ["A", "B", "C"]
array.each_with_index {|val, index| puts "#{val} => #{index}" }
Huellas dactilares:
A => 0
B => 1
C => 2
No estoy muy seguro de tu pregunta cuál estás buscando.
Había estado tratando de construir un menú (en Camping y Markaby ) usando un hash.
Cada elemento tiene 2 elementos: una etiqueta de menú y una URL , por lo que un hash parecía correcto, pero la URL ''/'' para ''Inicio'' siempre aparecía en último lugar (como era de esperar para un hash), así que los elementos del menú aparecían incorrectamente orden.
Usar 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
Agregar valores adicionales para cada elemento del menú (p. Ej., Como un nombre de ID de CSS) solo significa aumentar el valor del segmento. Así que, como un hash pero con grupos que consisten en cualquier número de elementos. Perfecto.
¡Así que esto es solo para decir gracias por insinuar inadvertidamente una solución!
Obvio, pero vale la pena decirlo: sugiero verificar si la longitud de la matriz es divisible por el valor del segmento.
La forma correcta es con la que se sienta más cómodo y que haga lo que usted quiere 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 cierta forma de hacer las cosas, hágalo, a menos que no funcione, entonces es el momento de encontrar una mejor manera.
Las otras respuestas están bien, pero quería señalar otra cosa periférica: las matrices están ordenadas, mientras que los hash no están en 1.8. (En Ruby 1.9, los hash están ordenados por el orden de inserción de las llaves). Por lo tanto, no tendría sentido antes de 1.9 iterar sobre un hash de la misma manera / secuencia que los arreglos, que siempre han tenido un orden definido. No sé cuál es el orden predeterminado para las matrices asociativas de PHP (al parecer, mi google fu no es lo suficientemente fuerte como para averiguarlo, tampoco), pero no sé cómo puede considerar las matrices regulares de PHP y las matrices asociativas de PHP para ser "el mismo" en este contexto, ya que el orden de las matrices asociativas parece indefinido.
Como tal, el camino de Ruby me parece más claro e intuitivo. :)
No estoy diciendo que Array
-> |value,index|
y Hash
-> |key,value|
no es una locura (vea el comentario de Horace Loeb), pero estoy diciendo que hay una forma sensata de esperar este arreglo.
Cuando trato con matrices, me enfoco en los elementos de la matriz (no en el índice porque el índice es transitorio). El método es cada uno con índice, es decir, cada índice +, o | cada, índice | o |value,index|
. Esto también es coherente con el índice que se ve como un argumento opcional, por ejemplo, | valor | es equivalente a | valor, índice = nil | que es consistente con | valor, índice |.
Cuando trato con hashes, a menudo me enfoco más en las claves que en los valores, y generalmente trato con claves y valores en ese orden, ya sea key => value
o hash[key] = value
.
Si desea escribir en el pato, entonces use explícitamente un método definido como mostró Brent Longborough, o un método implícito como mostró maxhawkins.
Ruby se trata de acomodar el lenguaje para adaptarse al programador, no de que el programador se adapte al lenguaje. Por eso hay tantas maneras. Hay tantas maneras de pensar en algo. En Ruby, tú eliges el más cercano y el resto del código generalmente cae de manera clara y concisa.
En cuanto a la pregunta original, "¿Cuál es la manera" correcta "de recorrer una matriz en Ruby?", Bueno, creo que la forma principal (es decir, sin un potente 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 tiene que ver con el potente azúcar sintáctico y el poder orientado a objetos, pero de todos modos aquí es el equivalente para los hashes, y las claves pueden ordenarse o no:
for key in hash.keys.sort
puts "hash[#{key.inspect}] = #{hash[key].inspect}"
end
Entonces, mi respuesta es: "La forma" correcta "de recorrer una matriz en Ruby depende de usted (es decir, del programador o del equipo de programación) y del proyecto". El mejor programador de Ruby hace la mejor elección (entre ellos, el poder sintáctico y / o el enfoque orientado a objetos). El mejor programador de Ruby sigue buscando más formas.
Ahora, quiero hacer otra pregunta, "¿Cuál es la manera" correcta "de recorrer 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 (para atrás):
(1..10).to_a.reverse.each{|i| puts "i=#{i}" }
Bueno, en realidad no me importa hacer eso demasiado, pero cuando estoy enseñando a ir hacia atrás, quiero mostrar a mis alumnos una simetría agradable (es decir, con una diferencia mínima, por ejemplo, solo agregar un reverso, o un paso -1, pero sin modificando cualquier otra cosa). Puedes hacer (por simetría):
(a=*1..10).each{|i| puts "i=#{i}" }
y
(a=*1..10).reverse.each{|i| puts "i=#{i}" }
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
Podrías hacer
class Range
def each_reverse(&block)
self.to_a.reverse.each(&block)
end
end
pero quiero enseñar Ruby puro en lugar de enfoques orientados a objetos (por el momento). Me gustaría iterar hacia atrás:
- sin crear una matriz (considere 0..1000000000)
- trabajando para cualquier rango (por ejemplo, cadenas, no solo enteros)
- sin utilizar ningún poder adicional orientado a objetos (es decir, sin modificación de clase)
Creo que esto es imposible sin definir un método pred
, lo que significa modificar la clase Range para usarlo. Si puede hacer esto, por favor hágamelo saber, de lo contrario se agradecería la confirmación de la imposibilidad, aunque sería decepcionante. Tal vez Ruby 1.9 aborde esto.
(Gracias por su tiempo en leer esto.)
Si utiliza la mezcla enumerable (como lo hace Rails) puede hacer algo similar al fragmento de código 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}" }
Menos parches de mono requerido.
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 aplanamiento que especifica la profundidad de la recuperación.
# 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. Normalmente, cuando tengo que inclinarme hacia atrás para iterar sobre algo, doy un paso atrás y me doy cuenta de que estoy atacando mal el problema.
Tratar de hacer lo mismo de forma consistente con matrices y hashes podría ser solo un olor a código, pero, a riesgo de que me marquen como un codificador de medio mono, si está buscando un comportamiento coherente, esto haría 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}"
}
Use each_with_index cuando necesite ambos.
ary.each_with_index { |val, idx| # ...