Hash anahtarına göre sırala, Ruby'de karma döndür


258

Bu, bir hashı sıralamanın ve Hash nesnesini (Array yerine) döndürmenin en iyi yolu olurdu:

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}

9
Kullanmadığınız eachveya each_pairüzerinde yineleme yapmadığınız sürece bir hashı sıralamanın çok avantajı olduğundan emin değilim . O zaman bile, muhtemelen hala anahtarları alıyorum, bunları sıralıyordum, daha sonra değerleri gerektiği gibi yakaladım. Bu, kodun eski Ruby'lerde doğru şekilde çalışmasını sağlar.
Tin Man

Yakut 1.9 da mantıklı. Ben db gelen tarihleri ​​(anahtarlar) olarak gruplandırılmış randevular bir koleksiyon vardı ve ben el ile yakut sıralanır. Örneğin. {"2012-09-22": [...], "2012-09-30": [...], "2012-10-12": [...]}
Adit Saxena

Evet, Hash [h.sort] işleminizi, anahtarları sıralamaktan ve sıralanan anahtarlar üzerinden tekrar karmaya erişmekten daha etkili buluyorum.
Douglas


3
Çözümünüzü düşünmek için birkaç yıl geçirdiniz, bir yanıtı kabul etmeye hazır mısınız? ;-)
Mark Thomas

Yanıtlar:


220

Ruby 2.1'de basit:

h.sort.to_h

@zachaysan ama işe yarıyor: h.sort{|a,z|a<=>z}.to_h(test 2.1.10, 2.3.3)
whitehat101

Haklısın. Bir hata vardı ( abir dizi, sadece anahtar). Yorumumu sildim.
zachaysan

Her ihtimale başkası bir yol arıyor sağlamalarının bir dizi sıralamak için, bu (h dizidir) hile olacaktır: h.map(&:sort).map(&:to_h).
JM Janzen

82

Not: Ruby> = 1.9.2'de bir sipariş koruma karması vardır: girilen sipariş anahtarları numaralandıkları sıra olacaktır. Aşağıdakiler eski sürümler veya geriye dönük uyumlu kod için geçerlidir.

Sıralanmış bir karma kavramı yoktur. Yani hayır, yaptığınız doğru değil.

Görüntülenmek üzere sıralanmasını istiyorsanız bir dize döndürün:

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

veya anahtarları sırayla istiyorsanız:

h.keys.sort

veya öğelere sırayla erişmek istiyorsanız:

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

ancak özet olarak, sıralı bir karma hakkında konuşmak mantıklı değildir. Gönderen docs , "ya anahtar veya değere göre bir karma çapraz sırası keyfi görünebilir ve genellikle kampanya siparişinde olmayacaktır." Bu nedenle, anahtarları belirli bir sırada karmaya eklemek yardımcı olmaz.


Doğru. Ruby'de sipariş edilen set işlevselliği kazanmak için RBtree mücevher kullanmanızı öneririz.
Aaron Scruggs

26
1.9.2'den itibaren karma kesici uç sırası korunacaktır. Bkz. Redmine.ruby-lang.org/issues/show/994
David

4
"1.9.2'den itibaren karma kesici uç sırası korunacaktır." Ve tatlı.
Tin Man

2
Benim ilk yorumum (komik hissediyorum): Örneğin, karma siparişine güvenmek, 1.9.2'den daha eski Ruby sürümleri için sessiz ve öngörülemez bir şekilde kırılacaktı.
Jo Liss

5
Bu sorunun OP sorusunun iki bölümünden birini bile yanıtlamadan yaklaşık 20 + 1'i nasıl elde etti? "1) Bu (OP örneği) bir hash, 2) sıralamanın ve Hash nesnesini döndürmenin en iyi yolu olur mu?" + 1'leri kıskanmıyorum :) Bu cevabı okuduktan hemen sonra hala orijinal sorularla kaldım. Ayrıca nokta sıralı bir karma diye bir şey yoksa, bu soruya seçilen cevap için yorumlara bakın stackoverflow.com/questions/489139/…
jj_

64

Hep kullandım sort_by. Sen sarmak gerekir #sort_byile çıktı Hash[]aksi takdirde diziler içeren bir dizi verir, bu çıkış bir karma yapmak. Alternatif olarak, bunu gerçekleştirmek için #to_hyöntemi bir k=>vyapıya (karma) dönüştürmek için tuples dizisinde çalıştırabilirsiniz .

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

Benzer bir soru, " Ruby Hash'i sayı değerine göre nasıl sıralayabilirim? "


8
sort_bykarma üzerinde kullanmak bir dizi döndürür. Tekrar karma olarak eşlemeniz gerekir. Hash[hsh.sort_by{|k,v| v}]
stevenspiel

1
evet, numaralandırıcı sınıfı hash'leri sanırım diziler olarak yorumlar
boulder_ruby

3
Sağ, sıralama değerlere geçerli: hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000}.
Cary Swoveland

1
Çağrı geldiğini hatırlatırız to_hsadece 2.1.0+ Ruby desteklenir
Phrogz

1
Yorumda bir yazım hatası var, düzeltme:sort_by{|k,v| v}.to_h)
Jitter

13

Hayır, değil (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)

Büyük karmalar için fark 10x ve daha fazla büyür


12

OP'de kendinize en iyi cevabı verdiniz: Hash[h.sort]Daha fazla olasılık için can atıyorsanız, orijinal karma işleminin sıralanmasını sağlamak için yerinde değişiklik:

h.keys.sort.each { |k| h[k] = h.delete k }

1
Merak edenler için bu , Ruby 1.9.3'teki stackoverflow.com/a/17331221/737303 adresindeki "anahtar sıralamasından" biraz daha hızlı çalışır .
azot

9

Hash anahtarına göre sırala , Ruby'de karma döndür

İle strüktür ve Hash # çeşit

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

Enumerable # sort_by

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

Varsayılan davranışla karma # sıralama

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 }

Not: <Yakut 2.1

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

Not:> Yakut 2.1

[["key", "value"]].to_h #=> {"key"=>"value"}

1
Eğer kullanmıyorsanız vbir altçizgi önekini hash.sort_by { |k, _v| k }.to_h
eklemelisiniz

5

ActiveSupport :: OrderedHash , yakut 1.9.2 kullanmak veya kendi geçici çözümlerinizi almak istemiyorsanız başka bir seçenektir.


Bağlantı koptu
Snake Sanders

3
Bazı tarihçilerin bunun geçmişte nasıl yapılabileceğini araştırmak istemesi durumunda Google'ı gösterme bağlantısını düzelttim. Ama şimdi buna gelen herkes Ruby'nin daha yeni bir sürümünü kullanıyor olmalı.
eremite


0

Aynı sorunu yaşadım (ekipmanımı isimlerine göre sıralamak zorunda kaldım) ve şu şekilde çözdüm:

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

@equipment benim model üzerinde inşa ve denetleyicime dönmek bir karma. .Sort öğesini çağırırsanız, karma değerini anahtar değerine göre sıralar.


-3

Önceki yazıda çözümü beğendim.

Mini bir sınıf yaptım, denir class AlphabeticalHash. Ayrıca, bir apbağımsız değişkeni, a Hash, giriş olarak kabul eden bir yöntem vardır ap variable. Akıntıya pp ( pp variable)

Ancak, alfabetik listede (anahtarları) yazdırmaya çalışacaktır. Başka kimse bunu kullanmak istiyorsa, bir mücevher olarak kullanılabilir, bu şekilde yükleyebilirsiniz:gem install alphabetical_hash

Benim için bu yeterince basit. Diğerlerinin daha fazla işlevselliğe ihtiyacı varsa, bana bildirin, gemiye dahil edeceğim.

EDIT: Kredi bana fikir veren Peter gidiyor . :)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.