In Ruby wird ein Array in einer der folgenden Formen angegeben ...
[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]
... was ist der beste Weg, um dies in einen Hash in Form von ...
{apple => 1, banana => 2}
HINWEIS : Eine präzise und effiziente Lösung finden Sie in der Antwort von Marc-André Lafortune unten.
Diese Antwort wurde ursprünglich als Alternative zu Ansätzen mit Abflachung angeboten, die zum Zeitpunkt des Schreibens am besten bewertet wurden. Ich hätte klarstellen sollen, dass ich dieses Beispiel nicht als Best Practice oder effizienten Ansatz präsentieren wollte. Die ursprüngliche Antwort folgt.
Warnung! Bei Lösungen mit Flatten bleiben Array-Schlüssel oder -Werte nicht erhalten!
Aufbauend auf der beliebten Antwort von @John Topley versuchen wir Folgendes:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
Dies wirft einen Fehler aus:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
Der Konstruktor erwartete ein Array mit gerader Länge (z. B. ['k1', 'v1,' k2 ',' v2 ']). Schlimmer ist, dass ein anderes Array, das auf eine gerade Länge abgeflacht ist, uns nur stillschweigend einen Hash mit falschen Werten gibt.
Wenn Sie Array-Schlüssel oder -Werte verwenden möchten, können Sie map verwenden :
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
Dadurch bleibt der Array-Schlüssel erhalten:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
h3 = Hash[*a3.flatten(1)]
Stattdessenh3 = Hash[*a3.flatten]
würde ein Fehler ausgelöst.to_h
ist besser.Einfach benutzen
Hash[*array_variable.flatten]
Beispielsweise:
Durch
Array#flatten(1)
die Verwendung wird die Rekursion begrenzt, sodassArray
Schlüssel und Werte wie erwartet funktionieren.quelle
Hash[*ary.flatten(1)]
, wodurch Array-Schlüssel und -Werte erhalten bleiben. Es ist die Rekursiveflatten
, die diese zerstört, was leicht zu vermeiden ist.Der beste Weg ist zu verwenden
Array#to_h
:Beachten Sie, dass
to_h
auch ein Block akzeptiert wird:Hinweis :
to_h
Akzeptiert einen Block in Ruby 2.6.0+. Für frühe Rubine kannst du meinenbackports
Edelstein verwenden undrequire 'backports/2.6.0/enumerable/to_h'
to_h
ohne Block wurde in Ruby 2.1.0 eingeführt.Vor Ruby 2.1 konnte man das weniger lesbare verwenden
Hash[]
:Seien Sie vorsichtig bei Lösungen
flatten
, die Probleme mit Werten verursachen können, die selbst Arrays sind.quelle
to_h
Methode besser als die obigen Antworten, weil sie die Absicht der Konvertierung nach dem Bearbeiten des Arrays ausdrückt .Array#to_h
nochEnumerable#to_h
im Kern Ruby 1.9.[[apple, 1], [banana, 2], [apple, 3], [banana, 4]]
und die Ausgabe als möchte{"apple" =>[1,3], "banana"=>[2,4]}
?Aktualisieren
Ruby 2.1.0 wird heute veröffentlicht . Und ich komme mit
Array#to_h
( Versionshinweise und Ruby-Doc ), das das Problem der Konvertierung von aArray
in a löstHash
.Beispiel für Ruby-Dokumente:
quelle
Die zweite Form ist einfacher:
a = Array, h = Hash, r = Rückgabewert-Hash (der, in dem wir uns ansammeln), i = Element im Array
Die beste Art und Weise, wie ich mir vorstellen kann, die erste Form zu machen, ist ungefähr so:
quelle
a.inject({})
Einzeiler, der flexiblere Wertzuweisungen ermöglicht.h = {}
aus dem zweiten Beispiel durch Verwendung vona.each_slice(2).inject({}) { |h,i| h[i.first] = i.last; h }
a.each_slice(2).to_h
Sie können ein 2D-Array auch einfach in Hash konvertieren, indem Sie:
quelle
Zusammenfassung & TL; DR:
Diese Antwort soll eine umfassende Zusammenfassung von Informationen aus anderen Antworten sein.
Die sehr kurze Version, angesichts der Daten aus der Frage und einiger Extras:
Diskussion und Details folgen.
Setup: Variablen
Um die Daten anzuzeigen, die wir im Voraus verwenden werden, werde ich einige Variablen erstellen, um verschiedene Möglichkeiten für die Daten darzustellen. Sie passen in folgende Kategorien:
Basierend auf dem, was direkt in der Frage stand, als
a1
unda2
:(Anmerkung: Ich nehme das an
apple
undbanana
sollte Variablen darstellen. Wie andere auch, werde ich von nun an Zeichenfolgen verwenden, damit Eingabe und Ergebnisse übereinstimmen.)Mehrwertige Schlüssel und / oder Werte wie folgt
a3
:In einigen anderen Antworten wurde eine andere Möglichkeit vorgestellt (die ich hier erläutere) - Schlüssel und / oder Werte können Arrays für sich sein:
Unausgeglichenes Array, wie
a4
:Aus gutem Grund dachte ich, ich würde eine für den Fall hinzufügen, dass wir möglicherweise eine unvollständige Eingabe haben:
Nun zur Arbeit:
Beginnend mit einem anfangs flachen Array,
a1
:Einige haben vorgeschlagen , mit
#to_h
(das zeigte sich in Ruby 2.1.0 und kann zurückportiert zu früheren Versionen). Bei einem anfangs flachen Array funktioniert dies nicht:Die Verwendung in
Hash::[]
Kombination mit dem Splat-Operator bewirkt:Das ist also die Lösung für den einfachen Fall, der durch dargestellt wird
a1
.Mit einem Array von Schlüssel / Wert-Paar-Arrays,
a2
:Mit einem Array von
[key,value]
Typ-Arrays gibt es zwei Möglichkeiten.Erstens
Hash::[]
funktioniert immer noch (wie bei*a1
):Und dann
#to_h
funktioniert jetzt auch:Also zwei einfache Antworten für den einfachen Fall eines verschachtelten Arrays.
Dies gilt auch für Sub-Arrays als Schlüssel oder Werte, wie bei
a3
:Aber Durian haben Spitzen (anomale Strukturen geben Probleme):
Wenn wir Eingabedaten erhalten haben, die nicht ausgeglichen sind, treten folgende Probleme auf
#to_h
:Funktioniert aber
Hash::[]
immer noch, indem nurnil
der Wert fürdurian
(und jedes andere Array-Element in a4, das nur ein 1-Wert-Array ist) festgelegt wird:Abflachen - mit neuen Variablen
a5
unda6
Einige andere Antworten
flatten
mit oder ohne1
Argument. Erstellen wir also einige neue Variablen:Ich habe mich
a4
aufgrund des Gleichgewichtsproblems, das sich bei uns zeigte, für die Verwendung als Basisdaten entschiedena4.to_h
. Ich glaube, ich rufe anflatten
könnte ein Ansatz sein, mit dem jemand versuchen könnte, das zu lösen, der wie folgt aussehen könnte.flatten
ohne Argumente (a5
):Auf einen naiven Blick scheint dies zu funktionieren - aber es hat uns mit den kernlosen Orangen auf den falschen Fuß gebracht und damit auch
3
einen Schlüssel unddurian
einen Wert geschaffen .Und das
a1
funktioniert wie bei einfach nicht:Also
a4.flatten
ist es für uns nicht nützlich, wir möchten es nur verwendenHash[a4]
Der
flatten(1)
Fall (a6
):Aber was ist mit nur teilweiser Abflachung? Es ist erwähnenswert, dass das Aufrufen
Hash::[]
mitsplat
dem teilweise abgeflachten Array (a6
) nicht dasselbe ist wie das Aufrufen vonHash[a4]
:Vorgeflachtes Array, immer noch verschachtelt (alternative Methode zum Abrufen
a6
):Aber was wäre, wenn wir auf diese Weise das Array überhaupt bekommen hätten? (Das heißt, vergleichbar mit
a1
unseren Eingabedaten - nur dieses Mal können einige der Daten Arrays oder andere Objekte sein.) Wir haben gesehen, dassHash[*a6]
dies nicht funktioniert, aber was ist, wenn wir immer noch das Verhalten erhalten möchten, bei dem die Das letzte Element (wichtig! siehe unten) diente als Schlüssel für einennil
Wert?In einer solchen Situation gibt es immer noch eine Möglichkeit, dies zu tun, indem
Enumerable#each_slice
wir als Elemente im äußeren Array zu Schlüssel / Wert- Paaren zurückkehren :Beachten Sie, dass wir dadurch ein neues Array erhalten, das nicht " identisch " ist
a4
, aber dieselben Werte hat :Und so können wir wieder verwenden
Hash::[]
:Aber es gibt ein Problem!
Es ist wichtig zu beachten, dass die
each_slice(2)
Lösung die Dinge nur dann wieder in Ordnung bringt, wenn der letzte Schlüssel derjenige war, dem ein Wert fehlt. Wenn wir später ein zusätzliches Schlüssel / Wert-Paar hinzufügen:Und die beiden Hashes, die wir daraus erhalten würden, unterscheiden sich in wichtigen Punkten:
(Hinweis: Ich verwende
awesome_print
'sap
nur, um die Darstellung der Struktur hier zu vereinfachen. Hierfür gibt es keine konzeptionellen Anforderungen.)Die
each_slice
Lösung für einen unsymmetrischen flachen Eingang funktioniert also nur, wenn sich das unsymmetrische Bit ganz am Ende befindet.Take-aways:
[key, value]
paarweise ein (ein Unterarray für jedes Element im äußeren Array).#to_h
oderHash::[]
beide.Hash::[]
funktioniert die Kombination mit splat (*
), solange die Eingänge ausgeglichen sind .value
Element das einzige ist, das fehlt.Randnotiz: Ich poste diese Antwort, weil ich der Meinung bin, dass es einen Mehrwert gibt - einige der vorhandenen Antworten enthalten falsche Informationen, und keine (die ich gelesen habe) gab eine so vollständige Antwort, wie ich es hier zu tun versuche. Ich hoffe es ist hilfreich. Trotzdem danke ich denen, die vor mir kamen, von denen einige Inspiration für Teile dieser Antwort lieferten.
quelle
An die Antwort anhängen, aber anonyme Arrays verwenden und Anmerkungen machen:
Nehmen Sie diese Antwort von innen heraus auseinander:
"a,b,c,d"
ist eigentlich eine Zeichenfolge.split
auf Kommas in ein Array.zip
das zusammen mit dem folgenden Array.[1,2,3,4]
ist ein tatsächliches Array.Das Zwischenergebnis ist:
Abflachen wandelt das dann um in:
und dann:
*["a",1,"b",2,"c",3,"d",4]
rollt das in"a",1,"b",2,"c",3,"d",4
die wir als Argumente für die
Hash[]
Methode verwenden können:was ergibt:
quelle
*
) und flatten:Hash[("a,b,c,d".split(',').zip([1,2,3,4]))]
=>{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
. Weitere Details in einer Antwort, die ich hinzugefügt habe.Wenn Sie ein Array haben, das so aussieht -
und Sie möchten, dass die ersten Elemente jedes Arrays die Schlüssel für den Hash werden und die restlichen Elemente zu Wert-Arrays, dann können Sie so etwas tun -
quelle
Ich bin mir nicht sicher, ob es der beste Weg ist, aber das funktioniert:
quelle
Wenn die numerischen Werte seq-Indizes sind, könnten wir einfachere Möglichkeiten haben ... Hier ist meine Codeübermittlung, My Ruby ist ein bisschen verrostet
quelle