¿Cómo obtener una salida específica iterando un hash en Ruby?

218

Quiero obtener una salida específica iterando un Ruby Hash.

Este es el Hash sobre el que quiero repetir:

hash = {
  1 => ['a', 'b'], 
  2 => ['c'], 
  3 => ['d', 'e', 'f', 'g'], 
  4 => ['h']
}

Este es el resultado que me gustaría obtener:

1-----

a

b

2-----

c

3-----

d 

e

f

g

4-----

h

En Ruby, ¿cómo puedo obtener esa salida con mi Hash?

pts
fuente
3
Si usted está iterando un hash y esperando que se puede pedir, es probable que tenga que utilizar algún otro tipo de colección
Allen Rice
¿Puedo pasar los valores hash como opción de botón de radio?
pts
estoy pasando el hash como opción de botón de radio ... pero para la primera opción estoy obteniendo el botón de radio, para otros valores no lo estoy obteniendo.
pts
1
@Allen: los hashes se ordenan en Ruby 1.9. Rails también proporciona un OrderedHash (que solo usa con moderación) si estás en Ruby <1.9. Ver culann.com/2008/01//rails-goodies-activesupportorderedhash
James A. Rosen

Respuestas:

323
hash.each do |key, array|
  puts "#{key}-----"
  puts array
end

Con respecto al orden que debo agregar, que en 1.8 los elementos se iterarán en orden aleatorio (bueno, en realidad en un orden definido por la función hash de Fixnum), mientras que en 1.9 se repetirá en el orden del literal.

sepp2k
fuente
1
aquí, ¿qué pasa si la clave no se usa en ninguna parte? . ¿Necesitamos poner una ?llave en lugar? Ej: ¿ |?, array|es esta sintaxis válida?
huzefa biyawarwala
1
@huzefabiyawarwala No, ?no es un nombre de variable válido en Ruby. Puedes usar _, pero no es necesario .
sepp2k
2
@huzefabiyawarwala Sí, puedes escribir|_, v|
sepp2k
1
¿Qué uso de matriz de nombre variable en lugar de v o valor?
jrhicks
1
@jrhicks Porque el OP tiene un hash cuyos valores son matrices.
Radon Rosborough
85

La forma más básica de iterar sobre un hash es la siguiente:

hash.each do |key, value|
  puts key
  puts value
end
tomascharad
fuente
Sí, esto tiene sentido. ¿Por qué la respuesta de @ sepp2k tiene un # {} en la clave?
committedandroider
Oh no importa. Vi que era para la interpolación de cadenas
committedandroider
48
hash.keys.sort.each do |key|
  puts "#{key}-----"
  hash[key].each { |val| puts val }
end
erik
fuente
18

Llamar a sort en un hash lo convierte en matrices anidadas y luego las ordena por clave, por lo que todo lo que necesita es esto:

puts h.sort.map {|k,v| ["#{k}----"] + v}

Y si realmente no necesita la parte "----", puede ser simplemente:

puts h.sort
glenn mcdonald
fuente
Las claves hash son números, por lo que '[k + "----"]' genera un TypeError (String no puede ser forzado a Fixnum). Necesitas '[k.to_s + "----"]'
glenn jackman el
Suficientemente cierto. Tenía letras en mi versión de prueba. Se corrigió el uso del aún mejor "# {k} ----".
glenn mcdonald
10

Mi solución de una línea:

hash.each { |key, array| puts "#{key}-----", array }

Creo que es bastante fácil de leer.

Elie Teyssedou
fuente
1

También puede refinar Hash::each para que sea compatible con la enumeración recursiva . Aquí está mi versión de Hash::each( Hash::each_pair) con soporte de bloque y enumerador :

module HashRecursive
    refine Hash do
        def each(recursive=false, &block)
            if recursive
                Enumerator.new do |yielder|
                    self.map do |key, value|
                        value.each(recursive=true).map{ |key_next, value_next| yielder << [[key, key_next].flatten, value_next] } if value.is_a?(Hash)
                        yielder << [[key], value]
                    end
                end.entries.each(&block)
            else
                super(&block)
            end
        end
        alias_method(:each_pair, :each)
    end
end

using HashRecursive

Aquí hay ejemplos de uso Hash::eachcon y sin recursivebandera:

hash = {
    :a => {
        :b => {
            :c => 1,
            :d => [2, 3, 4]
        },
        :e => 5
    },
    :f => 6
}

p hash.each, hash.each {}, hash.each.size
# #<Enumerator: {:a=>{:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}, :f=>6}:each>
# {:a=>{:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}, :f=>6}
# 2

p hash.each(true), hash.each(true) {}, hash.each(true).size
# #<Enumerator: [[[:a, :b, :c], 1], [[:a, :b, :d], [2, 3, 4]], [[:a, :b], {:c=>1, :d=>[2, 3, 4]}], [[:a, :e], 5], [[:a], {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}], [[:f], 6]]:each>
# [[[:a, :b, :c], 1], [[:a, :b, :d], [2, 3, 4]], [[:a, :b], {:c=>1, :d=>[2, 3, 4]}], [[:a, :e], 5], [[:a], {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}], [[:f], 6]]
# 6

hash.each do |key, value|
    puts "#{key} => #{value}"
end
# a => {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}
# f => 6

hash.each(true) do |key, value|
    puts "#{key} => #{value}"
end
# [:a, :b, :c] => 1
# [:a, :b, :d] => [2, 3, 4]
# [:a, :b] => {:c=>1, :d=>[2, 3, 4]}
# [:a, :e] => 5
# [:a] => {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}
# [:f] => 6

hash.each_pair(recursive=true) do |key, value|
    puts "#{key} => #{value}" unless value.is_a?(Hash)
end
# [:a, :b, :c] => 1
# [:a, :b, :d] => [2, 3, 4]
# [:a, :e] => 5
# [:f] => 6

Aquí hay un ejemplo de la pregunta en sí:

hash = {
    1   =>  ["a", "b"], 
    2   =>  ["c"], 
    3   =>  ["a", "d", "f", "g"], 
    4   =>  ["q"]
}

hash.each(recursive=false) do |key, value|
    puts "#{key} => #{value}"
end
# 1 => ["a", "b"]
# 2 => ["c"]
# 3 => ["a", "d", "f", "g"]
# 4 => ["q"]

También eche un vistazo a mi versión recursiva de Hash::merge( Hash::merge!) aquí .

MOPO3OB
fuente