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_hes mejor.Simplemente use
Hash[*array_variable.flatten]Por ejemplo:
El uso
Array#flatten(1)limita la recursividad para que lasArrayclaves 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 loflattenque los está destruyendo, lo cual es bastante fácil de evitar.La mejor manera es usar
Array#to_h:Tenga en cuenta que
to_htambién acepta un bloqueo:Nota :
to_hacepta un bloque en Ruby 2.6.0+; para los primeros rubíes puedes usar mibackportsgema yrequire 'backports/2.6.0/enumerable/to_h'to_hsin 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_hmétodo mejor que las respuestas anteriores porque expresa la intención de convertir después de operar en la matriz.Array#to_htampocoEnumerable#to_hestá 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 unArraya 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_hTambié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
a1ya2:(Nota: supongo que
appleybananaestaban 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_hfunciona 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 estableciendonilcomo el valor paradurian(y cualquier otro elemento de matriz en a4 que sea solo una matriz de valor 1):Aplanamiento: utilizando nuevas variables
a5ya6Algunas otras respuestas mencionadas
flatten, con o sin1argumento, así que creemos algunas variables nuevas:Elegí usar
a4como datos base debido al problema de equilibrio que tuvimos, que aparecióa4.to_h. Me imagino llamandoflattenpodría ser un enfoque que alguien podría usar para tratar de resolver eso, que podría ser el siguiente.flattensin 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
3una clave ydurianun valor .Y esto, como con
a1, simplemente no funciona:Así
a4.flattenque 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::[]usandosplaten 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
a1nuestros 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 unnilvalor?En tal situación, todavía hay una manera de hacer esto, utilizando
Enumerable#each_slicepara 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_slicesolució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_hoHash::[]ambos funcionarán.Hash::[]combinado con el splat (*) funcionará, siempre que las entradas estén equilibradas .valueelemento 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 cadenaspliten comas en una matriz.zipeso 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",4que 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