Dans Ruby, étant donné un tableau sous l'une des formes suivantes ...
[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]
... quel est le meilleur moyen de le convertir en hachage sous la forme de ...
{apple => 1, banana => 2}
REMARQUE : Pour une solution concise et efficace, veuillez consulter la réponse de Marc-André Lafortune ci-dessous.
Cette réponse a été proposée à l'origine comme une alternative aux approches utilisant flatten, qui étaient les plus votées au moment de la rédaction. J'aurais dû préciser que je n'avais pas l'intention de présenter cet exemple comme une meilleure pratique ou une approche efficace. La réponse originale suit.
Avertissement! Les solutions utilisant flatten ne conserveront pas les clés ou les valeurs du tableau!
En nous appuyant sur la réponse populaire de @John Topley, essayons:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
Cela génère une erreur:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
Le constructeur attendait un tableau de longueur paire (par exemple ['k1', 'v1,' k2 ',' v2 ']). Ce qui est pire, c'est qu'un tableau différent qui s'aplatit à une longueur égale nous donnerait simplement un hachage avec des valeurs incorrectes.
Si vous souhaitez utiliser des clés ou des valeurs de tableau, vous pouvez utiliser map :
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
Cela préserve la clé Array:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
h3 = Hash[*a3.flatten(1)]
au lieu deh3 = Hash[*a3.flatten]
cela, une erreur serait générée.to_h
est meilleure.Utilisez simplement
Hash[*array_variable.flatten]
Par exemple:
L'utilisation
Array#flatten(1)
limite la récursivité afin que lesArray
clés et les valeurs fonctionnent comme prévu.la source
Hash[*ary.flatten(1)]
, qui conservera les clés et les valeurs du tableau. C'est le récursifflatten
qui les détruit, ce qui est assez facile à éviter.La meilleure façon est d'utiliser
Array#to_h
:Notez que
to_h
accepte également un bloc:Remarque :
to_h
accepte un bloc dans Ruby 2.6.0+; pour les premiers rubis, vous pouvez utiliser mabackports
gemme etrequire 'backports/2.6.0/enumerable/to_h'
to_h
sans bloc a été introduit dans Ruby 2.1.0.Avant Ruby 2.1, on pouvait utiliser le moins lisible
Hash[]
:Enfin, méfiez-vous de toute solution utilisant
flatten
, cela pourrait créer des problèmes avec les valeurs qui sont elles-mêmes des tableaux.la source
to_h
méthode mieux que les réponses ci-dessus car elle exprime l'intention de convertir après avoir fonctionné sur le tableau.Array#to_h
ni l' autreEnumerable#to_h
n'est dans le noyau ruby 1.9.[[apple, 1], [banana, 2], [apple, 3], [banana, 4]]
et que je veux la sortie en tant que{"apple" =>[1,3], "banana"=>[2,4]}
?Mettre à jour
Ruby 2.1.0 est publié aujourd'hui . Et je suis livré avec
Array#to_h
( notes de publication et ruby-doc ), qui résout le problème de la conversionArray
d'unHash
.Exemple de documentation Ruby:
la source
La deuxième forme est plus simple:
a = tableau, h = hachage, r = hachage de la valeur de retour (celui dans lequel nous accumulons), i = élément du tableau
La meilleure façon dont je puisse penser de faire le premier formulaire est quelque chose comme ceci:
la source
a.inject({})
one-liner qui permet des attributions de valeur plus flexibles.h = {}
du deuxième exemple via l'utilisation de inject, se terminant para.each_slice(2).inject({}) { |h,i| h[i.first] = i.last; h }
a.each_slice(2).to_h
Vous pouvez également simplement convertir un tableau 2D en hachage en utilisant:
la source
Résumé & TL; DR:
Cette réponse espère être un récapitulatif complet des informations provenant d'autres réponses.
La version très courte, compte tenu des données de la question plus quelques extras:
Une discussion et des détails suivent.
Configuration: variables
Afin de montrer les données que nous allons utiliser à l'avance, je vais créer des variables pour représenter diverses possibilités pour les données. Ils entrent dans les catégories suivantes:
Basé sur ce qui était directement dans la question, comme
a1
eta2
:(Remarque: je présume que
apple
etbanana
étaient censés représenter des variables. Comme d'autres l'ont fait, j'utiliserai des chaînes à partir de maintenant afin que l'entrée et les résultats puissent correspondre.)Clés et / ou valeurs à valeurs multiples, comme
a3
:Dans d'autres réponses, une autre possibilité a été présentée (que je développe ici) - les clés et / ou les valeurs peuvent être des tableaux à elles seules:
Tableau déséquilibré, comme
a4
:Pour faire bonne mesure, j'ai pensé en ajouter un pour un cas où nous pourrions avoir une entrée incomplète:
Maintenant, au travail:
En commençant par un tableau initialement plat,
a1
:Certains ont suggéré d'utiliser
#to_h
(qui apparaissait dans Ruby 2.1.0 et peut être rétroporté vers des versions antérieures). Pour un tableau initialement plat, cela ne fonctionne pas:L'utilisation de
Hash::[]
combiné avec l' opérateur splat fait:Voilà donc la solution pour le cas simple représenté par
a1
.Avec un tableau de tableaux de paire de clés / valeur,
a2
:Avec un tableau de tableaux de
[key,value]
types, il existe deux façons de procéder.Tout d'abord,
Hash::[]
fonctionne toujours (comme il l'a fait avec*a1
):Et puis
#to_h
fonctionne aussi maintenant:Donc, deux réponses faciles pour le cas de tableau imbriqué simple.
Cela reste vrai même avec des sous-tableaux en tant que clés ou valeurs, comme avec
a3
:Mais les durians ont des pointes (les structures anormales posent des problèmes):
Si nous avons des données d'entrée qui ne sont pas équilibrées, nous rencontrerons des problèmes avec
#to_h
:Mais
Hash::[]
fonctionne toujours, il suffit de définirnil
comme valeur pourdurian
(et tout autre élément de tableau dans a4 qui n'est qu'un tableau à 1 valeur):Aplatissement - utilisation de nouvelles variables
a5
eta6
Quelques autres réponses mentionnées
flatten
, avec ou sans1
argument, créons donc de nouvelles variables:J'ai choisi d'utiliser
a4
comme données de base en raison du problème d'équilibre que nous avions, qui s'est manifestéa4.to_h
. Je me figure appelerflatten
pourrait être une approche que quelqu'un pourrait utiliser pour essayer de résoudre ce problème, ce qui pourrait ressembler à ce qui suit.flatten
sans arguments (a5
):D'un coup d'œil naïf, cela semble fonctionner - mais cela nous a mis du mauvais pied avec les oranges sans pépins, créant ainsi également
3
une clé etdurian
une valeur .Et cela, comme avec
a1
, ne fonctionne tout simplement pas:Cela
a4.flatten
ne nous est donc pas utile, nous voulons juste utiliserHash[a4]
Le
flatten(1)
cas (a6
):Mais qu'en est-il de l'aplatissement partiel? Il convient de noter que l'appel à l'
Hash::[]
aidesplat
du tableau partiellement aplati (a6
) n'est pas la même chose que l'appelHash[a4]
:Tableau pré-aplati, toujours imbriqué (autre façon d'obtenir
a6
):Mais que se passerait-il si c'était ainsi que nous avions obtenu le tableau en premier lieu? (C'est-à-dire, comparativement à
a1
, c'était nos données d'entrée - juste cette fois, certaines des données peuvent être des tableaux ou d'autres objets.) Nous avons vu queHash[*a6]
cela ne fonctionne pas, mais que se passerait-il si nous voulions toujours obtenir le comportement où le dernier élément (important! voir ci-dessous) a agi comme une clé pour unenil
valeur?Dans une telle situation, il existe toujours un moyen de le faire, en utilisant
Enumerable#each_slice
pour revenir aux paires clé / valeur en tant qu'éléments du tableau externe:Notez que cela finit par nous obtenir un nouveau tableau qui n'est pas " identique " à
a4
, mais qui a les mêmes valeurs :Et ainsi nous pouvons à nouveau utiliser
Hash::[]
:Mais il y a un problème!
Il est important de noter que la
each_slice(2)
solution ne ramène les choses à la bonne santé que si la dernière clé était celle qui manquait une valeur. Si nous avons ajouté plus tard une paire clé / valeur supplémentaire:Et les deux hachages que nous en tirerions sont différents de manière importante:
(Note: J'utilise
awesome_print
l »ap
. Juste pour le rendre plus facile de montrer la structure ici, il n'y a pas d' exigence conceptuelle pour cela)Donc, la
each_slice
solution à une entrée plate asymétrique ne fonctionne que si le bit asymétrique est à la toute fin.À emporter:
[key, value]
paires (un sous-tableau pour chaque élément du tableau externe).#to_h
ou l' autre fonctionnera ouHash::[]
les deux fonctionneront.Hash::[]
combiné avec le splat (*
) fonctionnera, tant que les entrées sont équilibrées .value
élément est le seul qui manque.Remarque: je poste cette réponse parce que j'estime qu'il y a une valeur à ajouter - certaines des réponses existantes ont des informations incorrectes, et aucune (que j'ai lu) n'a donné une réponse aussi complète que je m'efforce de le faire ici. J'espère que c'est utile. Je tiens néanmoins à remercier ceux qui sont venus avant moi, dont plusieurs ont inspiré des parties de cette réponse.
la source
Ajout à la réponse mais en utilisant des tableaux anonymes et en annotant:
En prenant cette réponse à part, en commençant par l'intérieur:
"a,b,c,d"
est en fait une chaîne.split
sur des virgules dans un tableau.zip
cela avec le tableau suivant.[1,2,3,4]
est un tableau réel.Le résultat intermédiaire est:
aplatir puis transforme cela en:
puis:
*["a",1,"b",2,"c",3,"d",4]
déroule ça dans"a",1,"b",2,"c",3,"d",4
que nous pouvons utiliser comme arguments de la
Hash[]
méthode:ce qui donne:
la source
*
) et aplatir:Hash[("a,b,c,d".split(',').zip([1,2,3,4]))]
=>{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
. Plus de détails dans une réponse que j'ai ajoutée.si vous avez un tableau qui ressemble à ceci -
et vous voulez que les premiers éléments de chaque tableau deviennent les clés du hachage et que le reste des éléments devienne des tableaux de valeurs, alors vous pouvez faire quelque chose comme ça -
la source
Je ne sais pas si c'est le meilleur moyen, mais cela fonctionne:
la source
Si les valeurs numériques sont des index seq, alors nous pourrions avoir des moyens plus simples ... Voici ma soumission de code, Mon Ruby est un peu rouillé
la source