Sortieren Sie den Hash nach Schlüssel und geben Sie den Hash in Ruby zurück

258

Wäre dies der beste Weg, um einen Hash zu sortieren und ein Hash-Objekt (anstelle von Array) zurückzugeben:

h = {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
# => {"a"=>1, "c"=>3, "b"=>2, "d"=>4}

Hash[h.sort]
# => {"a"=>1, "b"=>2, "c"=>3, "d"=>4}
Vincent
quelle
9
Ich bin mir nicht sicher, ob das Sortieren eines Hashs von großem Vorteil ist, es sei denn, Sie verwenden eachoder each_pairiterieren darüber. Selbst dann würde ich wahrscheinlich immer noch die Schlüssel greifen, diese sortieren und dann darüber iterieren und die Werte nach Bedarf abrufen. Dadurch wird sichergestellt, dass sich der Code auf älteren Rubinen korrekt verhält.
der Blechmann
Sinnvoll auch in Ruby 1.9. Ich hatte eine Sammlung von Terminen, gruppiert nach Datumsangaben (als Schlüssel), die von db kamen, und ich sortierte manuell nach Ruby. Z.B. {"2012-09-22": [...], "2012-09-30": [...], "2012-10-12": [...]}
Adit Saxena
Ja, ich finde Ihren Hash [h.sort] -Prozess effektiver als das Sortieren von Schlüsseln und den erneuten Zugriff auf den Hash über die sortierten Schlüssel.
Douglas
" Was ist der schnellste Weg, um einen Hash zu sortieren? Wird nützlich sein.
Der Blechmann
3
Sie hatten einige Jahre Zeit, um über Ihre Lösung nachzudenken. Sind Sie bereit, eine Antwort zu akzeptieren? ;-)
Mark Thomas

Antworten:

219

In Ruby 2.1 ist es einfach:

h.sort.to_h
Mark Thomas
quelle
@ Zachaysan, aber es funktioniert: h.sort{|a,z|a<=>z}.to_h(getestet 2.1.10, 2.3.3)
Whitehat101
@ whitehat101 Du hast recht. Ich hatte einen Fehler ( aist ein Array, nicht nur der Schlüssel). Ich habe meinen Kommentar gelöscht.
Zachaysan
Nur für den Fall, dass jemand anderes nach einer Möglichkeit sucht, ein Array von Hashes zu sortieren, reicht dies aus (wobei h das Array ist) : h.map(&:sort).map(&:to_h).
JM Janzen
82

Hinweis: Ruby> = 1.9.2 hat einen auftragserhaltenden Hash: Die eingefügten Auftragsschlüssel entsprechen der Reihenfolge, in der sie aufgelistet sind. Das Folgende gilt für ältere Versionen oder für abwärtskompatiblen Code.

Es gibt kein Konzept für einen sortierten Hash. Also nein, was du tust, ist nicht richtig.

Wenn Sie möchten, dass es zur Anzeige sortiert wird, geben Sie eine Zeichenfolge zurück:

"{" + h.sort.map{|k,v| "#{k.inspect}=>#{v.inspect}"}.join(", ") + "}"

oder, wenn Sie die Schlüssel in der richtigen Reihenfolge haben möchten:

h.keys.sort

oder, wenn Sie der Reihe nach auf die Elemente zugreifen möchten:

h.sort.map do |key,value|
  # keys will arrive in order to this block, with their associated value.
end

Zusammenfassend macht es jedoch keinen Sinn, über einen sortierten Hash zu sprechen. In den Dokumenten heißt es: "Die Reihenfolge, in der Sie einen Hash nach Schlüssel oder Wert durchlaufen, scheint willkürlich zu sein und liegt im Allgemeinen nicht in der Einfügereihenfolge." Das Einfügen von Schlüsseln in einer bestimmten Reihenfolge in den Hash hilft also nicht.

Peter
quelle
Das ist richtig. Ich würde vorschlagen, den RBtree-Edelstein zu verwenden, um geordnete Set-Funktionen in Ruby zu erhalten.
Aaron Scruggs
26
Ab 1.9.2 bleibt die Reihenfolge der Hash-Einfügungen erhalten. Siehe redmine.ruby-lang.org/issues/show/994
David
4
"Ab 1.9.2 wird die Reihenfolge der Hash-Einfügungen beibehalten.", Und es ist süß.
der Blechmann
2
Zu meinem ersten Kommentar (der sich lustig anfühlt): Wenn Sie sich beispielsweise auf die Hash-Reihenfolge verlassen, wird dies für Ruby-Versionen, die älter als 1.9.2 sind, lautlos und unvorhersehbar unterbrochen.
Jo Liss
5
Wie kam diese Antwort zu ungefähr 20 + 1, ohne auch nur einen der beiden Teile der OP-Frage zu beantworten? "1) Wäre das (OP-Beispiel) der beste Weg, um einen Hash zu sortieren, 2) und ein Hash-Objekt zurückzugeben"? Ich beneide nicht um +1 :) Kurz nachdem ich die Antwort gelesen habe, bleiben mir immer noch die ursprünglichen Fragen. Auch wenn der Punkt ist, dass es keinen sortierten Hash gibt, schauen Sie sich die Kommentare für die ausgewählte Antwort auf diese Frage an. Stackoverflow.com/questions/489139/…
jj_
64

Ich habe immer benutzt sort_by. Sie müssen die #sort_byAusgabe umschließen Hash[], damit sie einen Hash ausgibt, andernfalls wird ein Array von Arrays ausgegeben. Um dies zu erreichen, können Sie alternativ die #to_hMethode für das Array von Tupeln ausführen , um sie in eine k=>vStruktur (Hash) zu konvertieren .

hsh ={"a" => 1000, "b" => 10, "c" => 200000}
Hash[hsh.sort_by{|k,v| v}] #or hsh.sort_by{|k,v| v}.to_h

Es gibt eine ähnliche Frage in " Wie sortiere ich einen Ruby Hash nach Zahlenwert? ".

boulder_ruby
quelle
8
Wenn Sie sort_byeinen Hash verwenden, wird ein Array zurückgegeben. Sie müssten es erneut als Hash zuordnen. Hash[hsh.sort_by{|k,v| v}]
Stevenspiel
1
Ja, die Enumerator-Klasse interpretiert Hashes als Arrays, denke ich
boulder_ruby
3
Richtig, sortieren ist nach Werten : hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000}.
Cary Swoveland
1
Beachten Sie, dass das Aufrufen to_hnur in Ruby 2.1.0+
Phrogz
1
Es gibt einen Tippfehler im Kommentar, Korrektur:sort_by{|k,v| v}.to_h)
Jitter
13

Nein, ist es nicht (Ruby 1.9.x)

require 'benchmark'

h = {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
many = 100_000

Benchmark.bm do |b|
  GC.start

  b.report("hash sort") do
    many.times do
      Hash[h.sort]
    end
  end

  GC.start

  b.report("keys sort") do
    many.times do
      nh = {}
      h.keys.sort.each do |k|
        nh[k] = h[k]
      end
    end
  end
end

       user     system      total        real
hash sort  0.400000   0.000000   0.400000 (  0.405588)
keys sort  0.250000   0.010000   0.260000 (  0.260303)

Bei großen Hashes wächst der Unterschied auf das 10-fache und mehr

fl00r
quelle
12

Sie haben sich im OP die beste Antwort gegeben: Hash[h.sort]Wenn Sie sich nach mehr Möglichkeiten sehnen, finden Sie hier eine direkte Änderung des ursprünglichen Hashs, um ihn zu sortieren:

h.keys.sort.each { |k| h[k] = h.delete k }
Boris Stitnicky
quelle
1
Für Neugierige läuft dies etwas schneller als die "Schlüsselsortierung" in stackoverflow.com/a/17331221/737303 unter Ruby 1.9.3.
Stickstoff
9

Sortieren Sie den Hash nach Schlüssel und geben Sie den Hash in Ruby zurück

Mit Destrukturierung und Hash # Sortierung

hash.sort { |(ak, _), (bk, _)| ak <=> bk }.to_h

Enumerable # sort_by

hash.sort_by { |k, v| k }.to_h

Hash # sort mit Standardverhalten

h = { "b" => 2, "c" => 1, "a" => 3  }
h.sort         # e.g. ["a", 20] <=> ["b", 30]
hash.sort.to_h #=> { "a" => 3, "b" => 2, "c" => 1 }

Hinweis: <Ruby 2.1

array = [["key", "value"]] 
hash  = Hash[array]
hash #=> {"key"=>"value"}

Hinweis:> Ruby 2.1

[["key", "value"]].to_h #=> {"key"=>"value"}
Moriarty
quelle
1
Wenn Sie nicht verwenden v, sollten Sie einen Unterstrich hash.sort_by { |k, _v| k }.to_h
voranstellen
6

ActiveSupport :: OrderedHash ist eine weitere Option, wenn Sie Ruby 1.9.2 nicht verwenden oder eigene Problemumgehungen durchführen möchten.

Eremit
quelle
Die Verbindung ist unterbrochen
Snake Sanders
3
Ich habe den Link so korrigiert, dass er auf Google verweist, falls ein Historiker untersuchen möchte, wie dies in der Vergangenheit hätte geschehen können. Aber jeder, der jetzt darauf kommt, sollte eine neuere Version von Ruby verwenden.
Eremite
0
@ordered = {}
@unordered.keys.sort.each do |key|
  @ordered[key] = @unordered[key]
end
geboren
quelle
4
So würden Sie dies tun, wenn Ruby keine Methoden wie Hash # sort_by
boulder_ruby
0

Ich hatte das gleiche Problem (ich musste meine Geräte nach ihrem Namen sortieren) und löste es folgendermaßen:

<% @equipments.sort.each do |name, quantity| %>
...
<% end %>

@equipments ist ein Hash, den ich auf meinem Modell aufbaue und auf meinem Controller zurückgebe. Wenn Sie .sort aufrufen, wird der Hash nach seinem Schlüsselwert sortiert.

Gabriel Mesquita
quelle
-3

Mir hat die Lösung im vorherigen Beitrag gefallen.

Ich habe eine Miniklasse gemacht, sie genannt class AlphabeticalHash. Es hat auch eine Methode namens ap, die ein Argument, a Hash, als Eingabe akzeptiert : ap variable. Ähnlich wie pp ( pp variable)

Aber es wird (versuchen und) in alphabetischer Liste (seine Schlüssel) drucken. Keine Ahnung, wenn jemand anderes dies verwenden möchte, ist es als Juwel erhältlich. Sie können es als solches installieren:gem install alphabetical_hash

Für mich ist das einfach genug. Wenn andere mehr Funktionen benötigen, lassen Sie es mich wissen, ich werde es in den Edelstein aufnehmen.

EDIT: Dank geht an Peter , der mir die Idee gegeben hat. :) :)

Shevy
quelle