Sortir hash dengan kunci, kembalikan hash di Ruby

258

Apakah ini cara terbaik untuk mengurutkan hash dan mengembalikan objek Hash (bukan 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
sumber
9
Saya tidak yakin ada banyak keuntungan untuk menyortir hash, kecuali jika Anda menggunakan eachatau each_pairmengulanginya. Bahkan kemudian, saya mungkin masih mengambil kunci, mengurutkannya, lalu beralih ke mereka mengambil nilai-nilai yang diperlukan. Itu memastikan kode akan berperilaku benar pada Rubi yang lebih lama.
the Tin Man
Masuk akal di ruby ​​1.9 juga. Saya memiliki koleksi janji yang dikelompokkan berdasarkan tanggal (sebagai kunci) yang berasal dari db dan saya secara manual diurutkan melalui ruby. Misalnya. {"2012-09-22": [...], "2012-09-30": [...], "2012-10-12": [...]}
Adit Saxena
Ya, saya menemukan proses Hash [h.sort] Anda lebih efektif daripada tombol pengurutan kemudian mengakses lagi hash melalui kunci yang diurutkan.
Douglas
" Apa cara tercepat untuk menyortir Hash? Akan berguna.
Manusia Timah
3
Anda sudah beberapa tahun memikirkan solusi Anda, apakah Anda siap menerima jawaban? ;-)
Mark Thomas

Jawaban:

219

Di Ruby 2.1 itu sederhana:

h.sort.to_h
Mark Thomas
sumber
@zachaysan tetapi berhasil: h.sort{|a,z|a<=>z}.to_h(diuji 2.1.10, 2.3.3)
whitehat101
@ whitehat101 Anda benar. Saya memiliki bug ( aarray, bukan hanya kuncinya). Saya sudah menghapus komentar saya.
zachaysan
Hanya dalam kasus orang lain sedang mencari cara untuk mengurutkan array dari hash, ini akan melakukan trik (di mana h adalah array): h.map(&:sort).map(&:to_h).
JM Janzen
82

Catatan: Ruby> = 1.9.2 memiliki hash yang mempertahankan pesanan: kunci pesanan yang dimasukkan akan menjadi urutan yang disebutkan. Di bawah ini berlaku untuk versi yang lebih lama atau kode yang kompatibel dengan mundur.

Tidak ada konsep hash yang diurutkan. Jadi tidak, apa yang Anda lakukan tidak benar.

Jika Anda ingin diurutkan untuk ditampilkan, kembalikan string:

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

atau, jika Anda menginginkan kunci secara berurutan:

h.keys.sort

atau, jika Anda ingin mengakses elemen secara berurutan:

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

tetapi secara ringkas, tidak masuk akal untuk berbicara tentang hash yang diurutkan. Dari dokumen , "Urutan di mana Anda melintasi hash dengan salah satu kunci atau nilai mungkin tampak sewenang-wenang, dan umumnya tidak akan berada dalam urutan penyisipan." Jadi memasukkan kunci dalam urutan tertentu ke dalam hash tidak akan membantu.

Peter
sumber
Ini benar. Saya akan menyarankan menggunakan permata RBtree untuk mendapatkan fungsi yang diatur memerintahkan di ruby.
Aaron Scruggs
26
Mulai 1.9.2 pesanan memasukkan hash akan dipertahankan. Lihat redmine.ruby-lang.org/issues/show/994
David
4
"Mulai 1.9.2 pesanan hash insert akan dipertahankan.", Dan itu manis.
the Tin Man
2
Re komentar pertama saya (merasa lucu): Misalnya, mengandalkan pemesanan hash akan secara diam-diam dan tidak terduga rusak untuk versi Ruby yang lebih tua dari 1.9.2.
Jo Liss
5
Bagaimana jawaban ini didapat sekitar 20 +1 tanpa menjawab satu pun dari dua bagian pertanyaan OP? "1) Apakah itu (contoh OP) menjadi cara terbaik untuk menyortir hash, 2) dan mengembalikan objek Hash"? Saya tidak iri pada +1's :) hanya setelah itu membaca jawaban saya masih memiliki pertanyaan asli. Juga jika intinya adalah bahwa tidak ada yang namanya hash yang disortir pada komentar untuk jawaban yang dipilih untuk pertanyaan ini stackoverflow.com/questions/489139/…
jj_
64

Saya selalu menggunakan sort_by. Anda perlu membungkus #sort_byoutput dengan Hash[]untuk membuatnya menghasilkan hash, jika tidak output array array. Atau, untuk mencapai ini, Anda dapat menjalankan #to_hmetode pada array tuple untuk mengubahnya menjadi k=>vstruktur (hash).

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

Ada pertanyaan serupa di " Bagaimana cara mengurutkan Ruby Hash berdasarkan nilai angka? ".

boulder_ruby
sumber
8
menggunakan sort_byhash akan mengembalikan array. Anda perlu memetakannya sebagai hash lagi. Hash[hsh.sort_by{|k,v| v}]
stevenspiel
1
ya, kelas enumerator mengartikan hash sebagai array saya pikir
boulder_ruby
3
Benar, sort adalah pada nilai-nilai: hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000}.
Cary Swoveland
1
Perhatikan bahwa panggilan to_hhanya didukung di Ruby 2.1.0+
Phrogz
1
ada kesalahan ketik dalam komentar, koreksi:sort_by{|k,v| v}.to_h)
jitter
13

Tidak, bukan (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)

Untuk perbedaan hash besar akan tumbuh hingga 10x dan lebih banyak

fl00r
sumber
12

Anda memberikan jawaban terbaik untuk diri sendiri di OP: Hash[h.sort]Jika Anda menginginkan lebih banyak kemungkinan, berikut adalah modifikasi hash asli untuk membuatnya disortir:

h.keys.sort.each { |k| h[k] = h.delete k }
Boris Stitnicky
sumber
1
Bagi yang penasaran, ini berjalan sedikit lebih cepat daripada "sort key" di stackoverflow.com/a/17331221/737303 pada Ruby 1.9.3.
nitrogen
9

Sortir hash dengan kunci , kembalikan hash di Ruby

Dengan penghancuran dan semacam Hash

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

Dihitung # sort_by

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

Hash # urutkan dengan perilaku default

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 }

Catatan: <Ruby 2.1

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

Catatan:> Ruby 2.1

[["key", "value"]].to_h #=> {"key"=>"value"}
Moriarty
sumber
1
jika tidak menggunakan vAnda harus hash.sort_by { |k, _v| k }.to_h
awali
6

ActiveSupport :: OrderedHash adalah opsi lain jika Anda tidak ingin menggunakan ruby ​​1.9.2 atau memutar solusi Anda sendiri.

pendeta
sumber
Tautannya terputus
Snake Sanders
3
Saya memperbaiki tautan untuk menunjuk ke Google kalau-kalau beberapa sejarawan ingin meneliti bagaimana ini bisa dilakukan di masa lalu. Tetapi siapa pun yang datang dengan ini sekarang harus menggunakan versi Ruby yang lebih baru.
eremite
0
@ordered = {}
@unordered.keys.sort.each do |key|
  @ordered[key] = @unordered[key]
end
Beornborn
sumber
4
Ini adalah bagaimana Anda akan melakukan ini jika ruby ​​tidak memiliki metode seperti Hash # sort_by
boulder_ruby
0

Saya memiliki masalah yang sama (saya harus menyortir peralatan saya dengan nama mereka) dan saya menyelesaikannya seperti ini:

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

@ peralatan adalah hash yang saya bangun di model saya dan kembali pada controller saya. Jika Anda memanggil .sort maka akan mengurutkan hash berdasarkan nilai kuncinya.

Gabriel Mesquita
sumber
-3

Saya menyukai solusinya di posting sebelumnya.

Saya membuat kelas mini, menyebutnya class AlphabeticalHash. Ini juga memiliki metode yang disebut ap, yang menerima satu argumen, sebuah Hash, sebagai masukan: ap variable. Akin to pp ( pp variable)

Tetapi ia akan (mencoba dan) mencetak dalam daftar abjad (kuncinya). Entah apakah ada orang lain yang ingin menggunakan ini, tersedia sebagai permata, Anda dapat menginstalnya seperti:gem install alphabetical_hash

Bagi saya, ini cukup sederhana. Jika orang lain membutuhkan lebih banyak fungsi, beri tahu saya, saya akan memasukkannya ke dalam permata.

EDIT: Kredit untuk Peter , yang memberi saya ide. :)

shevy
sumber