2016-03-28 7 views
3

I den folgenden Ruby-Skript habe:Pushing Hash auf Array: letzte Hash Überschreiben vorherige Feldelemente

arr = ['bob', 'jack', 'smith'] 
array_of_hashes = Array.new 
hash = Hash.new 

arr.each do |item| 
    hash.clear 
    hash[:name] = item 
    array_of_hashes << hash 
end 

puts array_of_hashes 

Dies gibt einen Array von Hashes, das: Namen Schlüssel alle aus dem letzten Elemente sind.

[ 
    [0] { 
     :name => "smith" 
    }, 
    [1] { 
     :name => "smith" 
    }, 
    [2] { 
     :name => "smith" 
    } 
] 

Ich würde erwarten, dass es die folgenden zurückzukehren, aber ich bin fest versuchen, warum Element der letzte Hash, um herauszufinden, wird alle bisherigen Array-Elemente zu überschreiben:

[ 
    [0] { 
     :name => "bob" 
    }, 
    [1] { 
     :name => "jack" 
    }, 
    [2] { 
     :name => "smith" 
    } 
] 

EDIT: Vielen Dank für deine Antworten. Es ist schön, einige verschiedene Methoden zu haben, um das Gleiche zu erreichen. Ich habe jede Lösung getestet und jede ist großartig. Am Ende benutzte ich das, das meinem ursprünglichen Code am ähnlichsten war; aber mein Anwendungsfall ist ein einfaches lokales Skript - es wird nicht in einer industriellen App verwendet - in diesem Fall würde ich wahrscheinlich eine andere Lösung wählen.

Antwort

3

Sie sollten für jeden neuen Artikel etwas wie

arr = ['bob', 'jack', 'smith'] 
array_of_hashes = Array.new 

arr.each { |item| 
    hash = Hash.new 
    hash[:name] = item 
    array_of_hashes << hash 
} 

puts array_of_hashes 

mit einer neuen Instanz wird in das Array geschoben versuchen.

+0

Vielen Dank für Ihren Beitrag Ed; Ich mag es wirklich, wie dieser Code den Fehler in meinem ursprünglichen Code deutlich macht - das heißt, ich hatte nur einen Hash, mit dem ich es zu tun hatte, und dieser Code erstellt einen neuen Hash für jedes Array-Element. – singularity

+0

Ich bin froh, dass es geholfen hat, @singularity. Danke für die Verbesserung. –

+0

Ich habe diese Antwort jetzt akzeptiert, weil es das ist, was ich getan habe. Es war der schnellste Weg von meinem ursprünglichen Code zu einem funktionierenden Skript. "Hash.new" muss im Wesentlichen im Block "arr.each" stehen. '.clear' wird ebenfalls nicht benötigt. Obwohl es vielleicht nicht die eleganteste Lösung ist, ist es für mein Szenario am praktischsten und erledigt die Arbeit. – singularity

5

Sehen Sie sich Ihren Code genau an. Sie werden sehen, dass Sie nur eine einzige Instanz von Hash erstellt haben. Ihr Iterator entfernt frühere Bemühungen mit .clear, setzt das Element auf die aktuelle Iteration und fügt den Verweis auf dieses Objekt Hash zu verschiedenen Array-Positionen in array_of_hashes hinzu. Aber am Ende des Tages zeigen alle auf das gleiche Hash Objekt, und das Hash Objekt enthält nur das letzte, was Sie hineingesteckt haben.

4

Beachten Sie (und verstehen) die Antwort von @pjs und den Kommentar von @tadman, da sie erklären, warum Sie die Ergebnisse erhalten, die Sie bekommen.

Ich würde eigentlich stattdessen so etwas wie:

names = ['bob', 'jack', 'smith'] 

# combine the results into an array of Hashes 
array_of_hashes = 
    names.collect do |item| 
    { name: item } 
    end 

# output the results 
array_of_hashes # [{name: "bob"}, {name: "jack"},{name:"smith"}] 

Einige Hinweise:

  • Die Array#collect Methode gibt einen Array zurück, so dass Sie nicht manuell auf einen Array hinzufügen, müssen Sie‘ habe dein Selbst initialisiert.
  • Sie können nur einen neuen Hash für jedes Element zurück, die
+2

Der Schlüssel hier ist, dass 'Array << Hash 'eine * Objektreferenz * auf' Hash 'in das Array und nicht eine Kopie schiebt. – tadman

+0

Dies scheint die "rubinrote" Art zu sein, dies zu tun. – singularity

+0

Testen Sie den Code ohne die "puts" und sehen Sie ... es zeigt keine Ergebnisse, sorry. Deshalb habe ich deine Antwort bearbeitet. Probieren Sie es aus und bearbeiten Sie es dann selbst. –

2

im collect Block zurückgegeben werden ich

[:name].product(arr).map { |pair| Hash[[pair]] } 
    #=> [{:name=>"bob"}, {:name=>"jack"}, {:name=>"smith"}] 

oder (für Ruby v2 schreiben würde.0+)

[:name].product(arr).map { |pair| [pair].to_h } 

Die Schritte:

a = [:name].product(arr) 
    #=> [[:name, "bob"], [:name, "jack"], [:name, "smith"]] 
enum = a.map 
    # => #<Enumerator: [[:name, "bob"], [:name, "jack"], [:name, "smith"]]:map> 

beachte, dass das Verfahren durch das Senden zu Enumerator#eachenum wir das gewünschte Ergebnis erhalten:

enum.each { |pair| [pair].to_h } 
    #=> [{:name=>"bob"}, {:name=>"jack"}, {:name=>"smith"}] 

(Enumerator#each wird aufrufen Array#each weil a.class #=> Array.)

Enumerator#each setzt die Blockvariablen auf das erste Element der enum:

pair = enum.next 
    #=> [:name, "bob"] 

und dem Block-Berechnung durchgeführt wird:

[pair].to_h 
    #=> [[:name, "bob"]].to_h 
    #=> {:name=>"bob"} 

nächstes wird das zweite Element des enum wird [:name, "jack"], abgebildet auf:

Zuletzt,

pair = enum.next 
    #=> [:name, "smith"] 
[pair].to_h 
    #=> [[:name, "smith"]].to_h 
    #=> {:name=>"smith"} 
+0

Das scheint elegant. Es ist ein bisschen über meinem Kopf, aber für die n00bs (mich selbst eingeschlossen), um die Ausgabe zu sehen, können Sie dies "puts" oder zuweisen sie zu einem var - 'hash = [: name] .product (arr) .map {| Paar | [pair] .to_h} ' – singularity

+0

Ich werde am Morgen erarbeiten. Es ist weniger komplex als es zunächst scheinen mag. –

+0

Wow, danke für die Ausarbeitung! Das hilft, die Schritte ziemlich zu entmystifizieren – singularity

0

Sie können versuchen, diese zu

>>> names = ['bob', 'jack', 'smith'] 
>>> names.inject([]){|s,p| s << {name: p}} 
=> [{:name=>"bob"}, {:name=>"jack"}, {:name=>"smith"}]