Sắp xếp băm theo khóa, trả về hàm băm trong Ruby


258

Đây có phải là cách tốt nhất để sắp xếp một hàm băm và trả về đối tượng Hash (thay vì 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}

9
Tôi không chắc có nhiều lợi thế để sắp xếp một hàm băm, trừ khi bạn đang sử dụng eachhoặc each_pairlặp lại nó. Thậm chí sau đó, tôi có thể vẫn lấy các phím, sắp xếp chúng, sau đó lặp lại chúng để lấy các giá trị khi cần. Điều đó đảm bảo mã sẽ hoạt động chính xác trên các viên ruby ​​cũ.
Tin Man

Làm cho ý nghĩa trong ruby ​​1.9 quá. Tôi đã có một bộ sưu tập các cuộc hẹn được nhóm theo ngày (dưới dạng khóa) đến từ db và tôi tự sắp xếp thông qua ruby. Ví dụ. {"2012-09-22": [...], "2012-09-30": [...], "2012-10-12": [...]}
Saxena

Có, tôi thấy quy trình Hash [h.sort] của bạn hiệu quả hơn so với việc sắp xếp các khóa sau đó truy cập lại hàm băm các khóa đã sắp xếp.
Douglas


3
Bạn đã có một vài năm để suy nghĩ về giải pháp của mình, bạn đã sẵn sàng chấp nhận câu trả lời chưa? ;-)
Mark Thomas

Câu trả lời:


219

Trong Ruby 2.1 rất đơn giản:

h.sort.to_h

@zachaysan nhưng nó không hoạt động: h.sort{|a,z|a<=>z}.to_h(đã kiểm tra 2.1.10, 2.3.3)
whitehat101

@ trắnghat101 Bạn nói đúng. Tôi đã có một lỗi ( alà một mảng, không chỉ là chìa khóa). Tôi đã xóa bình luận của tôi.
zachaysan

Chỉ trong trường hợp người khác đang tìm cách sắp xếp một mảng băm, điều này sẽ thực hiện thủ thuật (trong đó h là mảng) : h.map(&:sort).map(&:to_h).
JM Janzen

82

Lưu ý: Ruby> = 1.9.2 có hàm băm giữ trật tự: các khóa thứ tự được chèn sẽ là thứ tự chúng được liệt kê. Dưới đây áp dụng cho các phiên bản cũ hơn hoặc mã tương thích ngược.

Không có khái niệm về một hàm băm được sắp xếp. Vì vậy, không, những gì bạn đang làm không đúng.

Nếu bạn muốn nó được sắp xếp để hiển thị, hãy trả về một chuỗi:

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

hoặc, nếu bạn muốn các phím theo thứ tự:

h.keys.sort

hoặc, nếu bạn muốn truy cập các yếu tố theo thứ tự:

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

nhưng tóm lại, thật vô nghĩa khi nói về một hàm băm được sắp xếp. Từ các tài liệu , "Thứ tự mà bạn duyệt qua hàm băm bằng khóa hoặc giá trị có thể có vẻ tùy ý và thường sẽ không theo thứ tự chèn." Vì vậy, chèn các khóa theo thứ tự cụ thể vào hàm băm sẽ không có ích.


Chính xác. Tôi sẽ đề nghị sử dụng đá quý RBtree để đạt được chức năng thiết lập theo thứ tự trong ruby.
Aaron Scruggs

26
Bắt đầu 1.9.2 thứ tự chèn băm sẽ được bảo tồn. Xem redmine.ruby-lang.org/issues/show/994
David

4
"Bắt đầu thứ tự băm 1.9.2 sẽ được giữ nguyên.", Và nó thật ngọt ngào.
Tin Man

2
Nhận xét đầu tiên của tôi (cảm thấy buồn cười): Ví dụ, việc dựa vào thứ tự băm sẽ phá vỡ âm thầm và không thể đoán trước đối với các phiên bản Ruby cũ hơn 1.9.2.
Jo Liss

5
Làm thế nào câu trả lời này có khoảng 20 + 1 mà không trả lời thậm chí không phải là một trong hai phần của câu hỏi OP? "1) Điều đó (ví dụ OP) có phải là cách tốt nhất để sắp xếp băm, 2) và trả về đối tượng Hash" không? Tôi không ghen tị + 1 giây :) chỉ sau khi đọc câu trả lời tôi vẫn còn những câu hỏi ban đầu. Ngoài ra, nếu vấn đề là không có thứ băm nào được sắp xếp, hãy xem các bình luận cho câu trả lời được chọn cho câu hỏi này stackoverflow.com/questions/489139/
Lỗi

64

Tôi đã luôn luôn sử dụng sort_by. Bạn cần phải bọc #sort_byđầu ra Hash[]để làm cho nó xuất ra một hàm băm, nếu không nó sẽ tạo ra một mảng các mảng. Ngoài ra, để thực hiện điều này, bạn có thể chạy #to_hphương thức trên mảng bộ dữ liệu để chuyển đổi chúng thành k=>vcấu trúc (hàm băm).

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

Có một câu hỏi tương tự trong " Cách sắp xếp Ruby Hash theo giá trị số? ".


8
sử dụng sort_bytrên một hàm băm sẽ trả về một mảng. Bạn sẽ cần phải vạch ra nó như là một hàm băm một lần nữa. Hash[hsh.sort_by{|k,v| v}]
stevenspiel 15/03/2016

1
vâng, lớp liệt kê diễn giải băm như mảng tôi nghĩ
boulder_ruby 17/03/2016

3
Đúng, sắp xếp là trên các giá trị : hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000}.
Cary Swoveland

1
Lưu ý rằng gọi điện to_hchỉ được hỗ trợ trong Ruby 2.1.0+
Phrogz

1
Có một lỗi đánh máy trong bình luận, sửa lỗi:sort_by{|k,v| v}.to_h)
jitter

13

Không, không phải (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)

Đối với băm lớn, sự khác biệt sẽ tăng lên gấp 10 lần và hơn thế nữa


12

Bạn đã đưa ra câu trả lời tốt nhất cho chính mình trong OP: Hash[h.sort]Nếu bạn khao khát có nhiều khả năng hơn, đây là sửa đổi tại chỗ của hàm băm ban đầu để sắp xếp nó:

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

1
Đối với người tò mò, điều này chạy nhanh hơn một chút so với "sắp xếp khóa" trong stackoverflow.com/a/17331221/737303 trên Ruby 1.9.3.
nitơ

9

Sắp xếp băm theo khóa , trả về hàm băm trong Ruby

Với phá hủy và Hash # sort

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

Vô số # sort_by

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

Hash # sort với hành vi mặc định

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 }

Lưu ý: <Ruby 2.1

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

Lưu ý:> Ruby 2.1

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

1
nếu không sử dụng, vbạn nên thêm tiền tố vào dấu gạch dướihash.sort_by { |k, _v| k }.to_h
silva96

6

ActiveSupport :: OrderedHash là một tùy chọn khác nếu bạn không muốn sử dụng ruby ​​1.9.2 hoặc cuộn các cách giải quyết của riêng bạn.


Liên kết bị hỏng
Snake Sanders

3
Tôi đã sửa liên kết để trỏ đến Google trong trường hợp một số nhà sử học muốn nghiên cứu làm thế nào điều này có thể được thực hiện trong quá khứ. Nhưng bất cứ ai đến đây bây giờ nên sử dụng phiên bản mới hơn của Ruby.
eremite

0
@ordered = {}
@unordered.keys.sort.each do |key|
  @ordered[key] = @unordered[key]
end

4
Đây là cách bạn sẽ làm điều này nếu ruby ​​không có các phương thức như Hash # sort_by
boulder_ruby

0

Tôi có cùng một vấn đề (tôi phải sắp xếp thiết bị của mình theo tên của họ) và tôi đã giải quyết như sau:

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

@equipments là một hàm băm mà tôi xây dựng trên mô hình của mình và trả về bộ điều khiển của tôi. Nếu bạn gọi .sort, nó sẽ sắp xếp hàm băm dựa trên giá trị khóa của nó.


-3

Tôi thích giải pháp trong bài trước.

Tôi làm một lớp nhỏ, gọi nó class AlphabeticalHash. Nó cũng có một phương thức được gọi ap, chấp nhận một đối số, a Hash, làm đầu vào : ap variable. Akin đến trang ( pp variable)

Nhưng nó sẽ (thử và) in trong danh sách theo thứ tự chữ cái (các phím của nó). Nếu không ai muốn sử dụng nó, nó có sẵn như một viên ngọc quý, bạn có thể cài đặt nó như sau:gem install alphabetical_hash

Đối với tôi, điều này là đủ đơn giản. Nếu người khác cần nhiều chức năng hơn, hãy cho tôi biết, tôi sẽ đưa nó vào đá quý.

EDIT: Tín dụng cho Peter , người đã cho tôi ý tưởng. :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.