En Ruby, dada una matriz en una de las siguientes formas ...
[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]
... cuál es la mejor manera de convertir esto en un hash en forma de ...
{apple => 1, banana => 2}
NOTA : Para una solución concisa y eficiente, consulte la respuesta de Marc-André Lafortune a continuación.
Esta respuesta se ofreció originalmente como una alternativa a los enfoques que usan aplanar, que fueron los más votados al momento de escribir. Debería haber aclarado que no tenía la intención de presentar este ejemplo como una mejor práctica o un enfoque eficiente. La respuesta original sigue.
¡Advertencia! ¡Las soluciones que usan flatten no conservarán las claves o valores de la matriz!
Sobre la base de la respuesta popular de @John Topley, intentemos:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
Esto arroja un error:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
El constructor esperaba una matriz de longitud par (por ejemplo, ['k1', 'v1,' k2 ',' v2 ']). Lo peor es que una matriz diferente que se aplanó a una longitud uniforme simplemente nos daría un Hash con valores incorrectos en silencio.
Si desea usar claves o valores de matriz, puede usar map :
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
Esto conserva la clave de matriz:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
h3 = Hash[*a3.flatten(1)]
lugar de loh3 = Hash[*a3.flatten]
cual arrojaría un error.to_h
es mejor.Simplemente use
Hash[*array_variable.flatten]
Por ejemplo:
El uso
Array#flatten(1)
limita la recursividad para que lasArray
claves y los valores funcionen como se esperaba.fuente
Hash[*ary.flatten(1)]
, que conservará las claves y los valores de la matriz. Es lo recursivo loflatten
que los está destruyendo, lo cual es bastante fácil de evitar.La mejor manera es usar
Array#to_h
:Tenga en cuenta que
to_h
también acepta un bloqueo:Nota :
to_h
acepta un bloque en Ruby 2.6.0+; para los primeros rubíes puedes usar mibackports
gema yrequire 'backports/2.6.0/enumerable/to_h'
to_h
sin bloque se introdujo en Ruby 2.1.0.Antes de Ruby 2.1, uno podría usar lo menos legible
Hash[]
:Finalmente, tenga cuidado con cualquier solución que use
flatten
, esto podría crear problemas con los valores que son matrices en sí.fuente
to_h
método mejor que las respuestas anteriores porque expresa la intención de convertir después de operar en la matriz.Array#to_h
tampocoEnumerable#to_h
está en core ruby 1.9.[[apple, 1], [banana, 2], [apple, 3], [banana, 4]]
y quiero la salida como{"apple" =>[1,3], "banana"=>[2,4]}
?Actualizar
Ruby 2.1.0 se lanza hoy . Y vengo con
Array#to_h
( notas de la versión y ruby-doc ), que resuelve el problema de convertir unArray
a unaHash
.Ruby docs ejemplo:
fuente
La segunda forma es más simple:
a = matriz, h = hash, r = hash de valor de retorno (en el que acumulamos), i = elemento en la matriz
La mejor manera en que puedo pensar en hacer la primera forma es algo como esto:
fuente
a.inject({})
one-liner que permite asignaciones de valor más flexibles.h = {}
del segundo ejemplo mediante el uso de inyectar, terminando cona.each_slice(2).inject({}) { |h,i| h[i.first] = i.last; h }
a.each_slice(2).to_h
También puede simplemente convertir una matriz 2D en hash usando:
fuente
Resumen y TL; DR:
Esta respuesta espera ser un resumen completo de la información de otras respuestas.
La versión muy corta, dada la información de la pregunta más un par de extras:
Discusión y detalles siguen.
Configuración: variables
Para mostrar los datos que usaremos por adelantado, crearé algunas variables para representar varias posibilidades para los datos. Encajan en las siguientes categorías:
Basado en lo que estaba directamente en la pregunta, como
a1
ya2
:(Nota: supongo que
apple
ybanana
estaban destinados a representar variables Como otros han hecho, voy a utilizar cuerdas de aquí en adelante por lo que la entrada y los resultados pueden igualar.).Claves y / o valores de valores múltiples, como
a3
:En algunas otras respuestas, se presentó otra posibilidad (que amplío aquí): las claves y / o valores pueden ser matrices por sí mismos:
Matriz desequilibrada, como
a4
:En buena medida, pensé que agregaría uno para un caso en el que podríamos tener una entrada incompleta:
Ahora, para trabajar:
Comenzando con una matriz inicialmente plana
a1
:Algunos han sugerido usar
#to_h
(que apareció en Ruby 2.1.0, y puede ser compatible con versiones anteriores). Para una matriz inicialmente plana, esto no funciona:El uso
Hash::[]
combinado con el operador splat hace:Entonces esa es la solución para el caso simple representado por
a1
.Con una matriz de claves arrays valor / pair,
a2
:Con una matriz de matrices de
[key,value]
tipos, hay dos formas de hacerlo.Primero,
Hash::[]
todavía funciona (como lo hizo con*a1
):Y luego también
#to_h
funciona ahora:Entonces, dos respuestas fáciles para el caso de matriz anidada simple.
Esto sigue siendo cierto incluso con subconjuntos como claves o valores, como con
a3
:Pero los durianos tienen picos (las estructuras anómalas dan problemas):
Si hemos recibido datos de entrada que no están equilibrados, tendremos problemas con
#to_h
:Pero
Hash::[]
aún funciona, solo estableciendonil
como el valor paradurian
(y cualquier otro elemento de matriz en a4 que sea solo una matriz de valor 1):Aplanamiento: utilizando nuevas variables
a5
ya6
Algunas otras respuestas mencionadas
flatten
, con o sin1
argumento, así que creemos algunas variables nuevas:Elegí usar
a4
como datos base debido al problema de equilibrio que tuvimos, que aparecióa4.to_h
. Me imagino llamandoflatten
podría ser un enfoque que alguien podría usar para tratar de resolver eso, que podría ser el siguiente.flatten
sin argumentos (a5
):A simple vista, esto parece funcionar, pero nos puso en el camino equivocado con las naranjas sin semillas, por lo que también es
3
una clave ydurian
un valor .Y esto, como con
a1
, simplemente no funciona:Así
a4.flatten
que no es útil para nosotros, solo queremos usarHash[a4]
El
flatten(1)
caso (a6
):Pero, ¿qué pasa con el aplanamiento parcial? Vale la pena señalar que llamar
Hash::[]
usandosplat
en la matriz parcialmente aplanada (a6
) no es lo mismo que llamarHash[a4]
:Matriz previamente aplanada, todavía anidada (forma alternativa de obtener
a6
):Pero, ¿qué pasaría si así fue como obtuvimos la matriz en primer lugar? (Es decir, en comparación con
a1
nuestros datos de entrada, solo que esta vez algunos de los datos pueden ser matrices u otros objetos). Hemos visto que esoHash[*a6]
no funciona, pero ¿y si todavía quisiéramos obtener el comportamiento donde ¿El último elemento (importante! ver más abajo) actuó como clave para unnil
valor?En tal situación, todavía hay una manera de hacer esto, utilizando
Enumerable#each_slice
para volver a los pares clave / valor como elementos en la matriz externa:Tenga en cuenta que esto termina obteniendo una nueva matriz que no es " idéntica "
a4
, pero tiene los mismos valores :Y así podemos usar nuevamente
Hash::[]
:Pero hay un problema!
Es importante tener en cuenta que la
each_slice(2)
solución solo devuelve las cosas a la cordura si la última clave fue la que falta un valor. Si luego agregamos un par clave / valor adicional:Y los dos hashes que obtendríamos de esto son diferentes en formas importantes:
(Nota: estoy usando
awesome_print
'sap
. Sólo para que sea más fácil para mostrar la estructura aquí, no hay requisito conceptual para esto)Entonces, la
each_slice
solución para una entrada plana no balanceada solo funciona si el bit no balanceado está al final.Para llevar:
[key, value]
pares (una submatriz para cada elemento en la matriz externa).#to_h
oHash::[]
ambos funcionarán.Hash::[]
combinado con el splat (*
) funcionará, siempre que las entradas estén equilibradas .value
elemento es el único que falta.Nota al margen: estoy publicando esta respuesta porque siento que hay valor para agregar: algunas de las respuestas existentes tienen información incorrecta, y ninguna (que leí) dio una respuesta tan completa como me estoy esforzando por hacer aquí. Espero que sea útil. Sin embargo, doy gracias a los que vinieron antes que yo, varios de los cuales inspiraron algunas partes de esta respuesta.
fuente
Anexando la respuesta pero usando matrices anónimas y anotaciones:
Tomando esa respuesta aparte, comenzando desde adentro:
"a,b,c,d"
es en realidad una cadenasplit
en comas en una matriz.zip
eso junto con la siguiente matriz.[1,2,3,4]
es una matriz realEl resultado intermedio es:
aplanar luego transforma eso a:
y entonces:
*["a",1,"b",2,"c",3,"d",4]
desenrolla eso en"a",1,"b",2,"c",3,"d",4
que podemos usar como argumentos del
Hash[]
método:cuyos rendimientos:
fuente
*
) y flatten:Hash[("a,b,c,d".split(',').zip([1,2,3,4]))]
=>{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
. Más detalles en una respuesta que agregué.si tiene una matriz que se ve así:
y desea que los primeros elementos de cada matriz se conviertan en las claves para el hash y el resto de los elementos se conviertan en matrices de valores, entonces puede hacer algo como esto:
fuente
No estoy seguro de si es la mejor manera, pero esto funciona:
fuente
Si los valores numéricos son índices seq, entonces podríamos tener formas más simples ... Aquí está mi envío de código, My Ruby está un poco oxidado
fuente