Sortuj skrót według klucza, zwróć skrót w Ruby

257

Czy byłby to najlepszy sposób na posortowanie skrótu i ​​zwrócenie obiektu Hash (zamiast Array):

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
źródło
9
Nie jestem pewien, czy sortowanie skrótu ma dużą zaletę, chyba że używasz go eachlub each_pairiterujesz. Nawet wtedy pewnie nadal chwytałbym klucze, sortowałam je, a następnie powtarzałam je, chwytając wartości w razie potrzeby. To gwarantuje, że kod będzie działał poprawnie na starszych Rubinach.
Tin Man
Ma to również sens w Ruby 1.9. Miałem zbiór spotkań pogrupowanych według dat (jako kluczy) pochodzących z bazy danych i ręcznie posortowałem według ruby. Na przykład. {"2012-09-22": [...], "2012-09-30": [...], "2012-10-12": [...]}
Adit Saxena
Tak, uważam, że proces skrótu [h.sort] jest bardziej efektywny niż sortowanie kluczy, a następnie ponowne uzyskiwanie dostępu do skrótu przez posortowane klucze.
Douglas,
3
Miałeś kilka lat na przemyślenie swojego rozwiązania, czy jesteś gotowy przyjąć odpowiedź? ;-)
Mark Thomas

Odpowiedzi:

219

W Ruby 2.1 jest to proste:

h.sort.to_h
Mark Thomas
źródło
@zachaysan, ale to działa: h.sort{|a,z|a<=>z}.to_h(testowane 2.1.10, 2.3.3)
whitehat101
@ whitehat101 Masz rację. Miałem błąd ( ato tablica, nie tylko klucz). Usunąłem mój komentarz.
zachaysan
Tylko w przypadku, ktoś inny szuka sposobu, aby posortować tablicę skrótów, będzie to rade (gdzie h jest tablica) h.map(&:sort).map(&:to_h).
JM Janzen,
82

Uwaga: Ruby> = 1.9.2 ma hash zachowujący porządek: wstawiane klucze zamówień będą kolejnością, w której są wyliczane. Poniższe dotyczy starszych wersji lub kodu kompatybilnego wstecz.

Nie ma koncepcji posortowanego skrótu. Więc nie, to co robisz jest niewłaściwe.

Jeśli chcesz posortować do wyświetlenia, zwróć ciąg:

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

lub, jeśli chcesz klucze w kolejności:

h.keys.sort

lub, jeśli chcesz uzyskać dostęp do elementów w kolejności:

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

ale podsumowując, nie ma sensu mówić o posortowanym haszu. Z dokumentów : „Kolejność przechodzenia przez skrót lub klucz lub wartość może wydawać się dowolna i na ogół nie będzie w kolejności wstawiania”. Więc wstawienie kluczy w określonej kolejności do skrótu nie pomoże.

Piotr
źródło
To jest poprawne. Sugerowałbym użycie klejnotu RBtree, aby uzyskać funkcjonalność zestawu uporządkowanego w rubinie.
Aaron Scruggs,
26
Począwszy od 1.9.2 kolejność wstawiania skrótu zostanie zachowana. Zobacz redmine.ruby-lang.org/issues/show/994
David
4
„Począwszy od wersji 1.9.2 kolejność wstawiania skrótu zostanie zachowana.” I jest ładna.
Tin Man,
2
Re mój pierwszy komentarz (zabawne): Na przykład poleganie na porządkowaniu haszowania po cichu i nieprzewidziany przerwie dla wersji Ruby starszych niż 1.9.2.
Jo Liss,
5
Jak ta odpowiedź uzyskała około 20 + 1 bez odpowiedzi nawet na jedną z dwóch części pytania PO? „1) Czy byłby to (na przykład OP) najlepszy sposób sortowania skrótu, 2) i zwracania obiektu Hash”? Nie zazdroszczę + 1-ek :) zaraz po przeczytaniu odpowiedzi wciąż mam oryginalne pytania. Także jeśli chodzi o to, że nie ma czegoś takiego jak posortowany skrót, spójrz na komentarze dla wybranej odpowiedzi stackoverflow.com/questions/489139/...
jj_
64

Zawsze używałem sort_by. Musisz owinąć dane #sort_bywyjściowe, Hash[]aby uzyskać wynik mieszania, w przeciwnym razie wyprowadza tablicę tablic. Alternatywnie, aby to osiągnąć, możesz uruchomić #to_hmetodę na tablicy krotek, aby przekonwertować je na k=>vstrukturę (skrót).

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

Podobne pytanie znajduje się w „ Jak posortować Ruby Hash według wartości liczbowej? ”.

boulder_ruby
źródło
8
użycie sort_byskrótu zwróci tablicę. Będziesz musiał ponownie zmapować go jako skrót. Hash[hsh.sort_by{|k,v| v}]
stevenspiel
1
tak, klasa wyliczająca interpretuje skróty jako tablice, myślę
boulder_ruby
3
Prawo, porządek jest na wartości: hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000}.
Cary Swoveland,
1
Zauważ, że dzwonienie to_hjest obsługiwane tylko w Ruby 2.1.0+
Phrogz
1
w komentarzu jest literówka, poprawka:sort_by{|k,v| v}.to_h)
fluktuacja
13

Nie, to nie jest (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)

W przypadku dużych skrótów różnica wzrośnie do 10x i więcej

fl00r
źródło
12

Dałeś najlepszą odpowiedź sobie w OP: Hash[h.sort]jeśli pragniesz więcej możliwości, oto modyfikacja oryginalnego skrótu na miejscu, aby go posortować:

h.keys.sort.each { |k| h[k] = h.delete k }
Boris Stitnicky
źródło
1
Dla ciekawskich działa to nieco szybciej niż „sortowanie kluczy” w stackoverflow.com/a/17331221/737303 na Ruby 1.9.3.
azot
9

Sortuj skrót według klucza , zwróć skrót w Ruby

Z destrukcją i sortowaniem Hash

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

Wyliczalny # sort_by

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

Hash # sortuj z zachowaniem domyślnym

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 }

Uwaga: <Ruby 2.1

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

Uwaga:> Ruby 2.1

[["key", "value"]].to_h #=> {"key"=>"value"}
Moriarty
źródło
1
jeśli nie używasz v, hash.sort_by { |k, _v| k }.to_h
poprzedź
6

ActiveSupport :: OrdersHash to kolejna opcja, jeśli nie chcesz używać Ruby 1.9.2 lub tworzyć własnych obejść.

eremita
źródło
Link jest zerwany
Snake Sanders
3
Naprawiłem link prowadzący do Google na wypadek, gdyby jakiś historyk chciał zbadać, jak można to zrobić w przeszłości. Ale każdy, kto przyjdzie na to teraz, powinien używać nowszej wersji Ruby.
eremite
0
@ordered = {}
@unordered.keys.sort.each do |key|
  @ordered[key] = @unordered[key]
end
urodzony
źródło
4
Tak byś zrobił, gdyby Ruby nie miał metod takich jak Hash # sort_by
boulder_ruby
0

Miałem ten sam problem (musiałem posortować sprzęt według nazwy) i rozwiązałem w ten sposób:

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

@equipments to skrót, który buduję na moim modelu i zwracam na kontrolerze. Jeśli wywołasz .sort, posortuje skrót według jego kluczowej wartości.

Gabriel Mesquita
źródło
-3

Podobało mi się rozwiązanie we wcześniejszym poście.

Zrobiłem mini-klasę class AlphabeticalHash. Ma również sposób zwany ap, która przyjmuje jeden argument, Hashjako wejście: ap variable. Podobne do pp ( pp variable)

Ale to (spróbuje) wydrukować na liście alfabetycznej (jej klucze). Nie wiem, czy ktokolwiek chce tego użyć, jest dostępny jako klejnot, możesz go zainstalować w ten sposób:gem install alphabetical_hash

Dla mnie jest to dość proste. Jeśli inni potrzebują więcej funkcji, daj mi znać, włączę ją do klejnotu.

EDYCJA: Podziękowania należą się Peterowi , który podsunął mi pomysł. :)

shevy
źródło