Helpex - Trao đổi & giúp đỡ Đăng nhập
23

Tôi đang cố gắng sắp xếp một Hash theo thứ tự bảng chữ cái theo khóa, nhưng dường như tôi không thể tìm ra cách thực hiện điều đó nếu không tạo lớp Sắp xếp của riêng mình. Tôi tìm thấy mã dưới đây để sắp xếp theo giá trị nếu đó là một số nguyên và tôi đang cố gắng sửa đổi nó nhưng không gặp may.

temp["ninjas"]=36
temp["pirates"]=12
temp["cheese"]=222
temp.sort_by { |key, val| key }

Mục tiêu của tôi là đặt hàng Hash theo khóa sau đó xuất ra các giá trị. Tôi sẽ phải làm điều này nhiều lần với các lệnh băm khác nhau nhưng có cùng giá trị.

23 hữu ích 3 bình luận 30k xem chia sẻ
43

Giả sử bạn muốn đầu ra là một hàm băm sẽ lặp lại qua các khóa theo thứ tự được sắp xếp, thì bạn đã gần hoàn thành. Hash#sort_bytrả về một Arraytrong số Arrays và các mảng bên trong đều là hai phần tử.

Ruby's Hashcó một hàm tạo có thể sử dụng đầu ra này.

Thử cái này:

temp = Hash[ temp.sort_by { |key, val| key } ]

hay ngắn gọn hơn

temp = temp.sort_by { |key| key }.to_h

Nếu băm của bạn có các loại khóa hỗn hợp, điều này sẽ không hoạt động ( ví dụ như Ruby sẽ không tự động sắp xếp giữa Strings và Symbols) và bạn sẽ nhận được thông báo lỗi như so sánh Symbol với String không thành công (ArgumentError) . Nếu vậy, bạn có thể thay đổi những điều trên thành

temp = Hash[ temp.sort_by { |key, val| key.to_s } ] 

để giải quyết vấn đề. Tuy nhiên, hãy lưu ý rằng các khóa sẽ vẫn giữ nguyên kiểu ban đầu của chúng, điều này có thể gây ra vấn đề với các giả định trong mã sau này. Ngoài ra, hầu hết các lớp tích hợp đều hỗ trợ một .to_sphương thức, vì vậy bạn có thể nhận được kết quả không mong muốn từ đó (chẳng hạn như thứ tự sắp xếp không mong muốn cho các phím số hoặc các kiểu không mong muốn khác).

Ngoài ra , bạn có thể chuyển đổi các khóa thành Stringsmột cái gì đó như thế này:

temp = Hash[ temp.map { |key, val| [key.to_s, val] }.sort ] 

. . . mặc dù cách tiếp cận này sẽ làm mất thông tin về loại khóa ban đầu khiến không thể quay trở lại dữ liệu ban đầu một cách đáng tin cậy.

43 hữu ích 5 bình luận chia sẻ
8
sorted_by_key = Hash[original_hash.sort]

sẽ tạo một Hash mới bằng cách chèn các khóa / giá trị original_hashtheo thứ tự bảng chữ cái của khóa. Các hàm băm trong Ruby 2.x ghi nhớ thứ tự chèn của chúng, vì vậy hàm băm mới này sẽ xuất hiện được sắp xếp theo khóa nếu bạn liệt kê hoặc xuất nó.

Nếu bạn chèn nhiều phần tử hơn theo thứ tự không phải bảng chữ cái, điều này tất nhiên sẽ không giữ.

Ngoài ra, điều này giả định các khóa băm ban đầu đều có thể sắp xếp / so sánh được.

8 hữu ích 1 bình luận chia sẻ
6

Ruby's Hash ghi nhớ thứ tự chèn của nó ngày nay, nhưng những viên Rubies trước đó <v1.9 thì không. Tuy nhiên, đừng bận tâm đến việc phân loại một hàm băm vì không có lợi khi làm như vậy vì về cơ bản Hash là một cấu trúc truy cập ngẫu nhiên. Điều đó có nghĩa là tất cả các phần tử đều có thể truy cập bất kỳ lúc nào và nó sẽ không tạo ra sự khác biệt cho dù là phần tử đầu tiên hay cuối cùng, bạn có thể truy cập nó giống nhau.

Điều đó không giống như Mảng hoạt động giống như một tệp tuần tự / văn bản hoặc một chuỗi hoặc một hàng đợi và bạn phải truy cập nó một cách tuyến tính bằng cách lặp lại nó, tại thời điểm đó, thứ tự của các phần tử tạo ra sự khác biệt lớn.

Vì vậy, với Hash, hãy lấy các khóa, sắp xếp chúng và lặp lại danh sách các khóa hoặc sử dụng values_atđể truy xuất tất cả các giá trị cùng một lúc. Ví dụ:

hash = {
    'z' => 9,
    'a' => 1
}

sorted_keys = hash.keys.sort # => ["a", "z"]
sorted_keys.each do |k|
  puts hash[k]
end
# >> 1
# >> 9

hash.values_at(*sorted_keys) # => [1, 9]

Một số ngôn ngữ thậm chí sẽ không cho phép bạn sắp xếp hàm băm và truy cập nó thông qua danh sách các khóa được sắp xếp là cách duy nhất để trích xuất các phần tử theo thứ tự, vì vậy có lẽ bạn không nên có thói quen dựa vào thứ tự của các cặp khóa / giá trị và thay vào đó dựa vào các khóa.

6 hữu ích 5 bình luận chia sẻ
0

Bạn có thể tạo một băm trống mới để giữ dữ liệu băm đã được sắp xếp. Lặp lại thông qua mảng được trả về và tải dữ liệu vào hàm băm mới để giữ dữ liệu băm đã được sắp xếp.

temp = {}
temp["ninjas"]=36
temp["pirates"]=12
temp["cheese"]=222 
temp = temp.sort_by { |key, val| key }

temp_sorted = {}
temp.each { |sub_arr| temp_sorted[sub_arr[0]] = sub_arr[1] } 
temp = temp_sorted

nhiệt độ bây giờ bằng {"cheese" => 222, "ninjas" => 36, "pirates" => 12}

0 hữu ích 0 bình luận chia sẻ
0

Ngoài câu trả lời của Neil Slater , sử dụng Hash#sort_byphương pháp (rất hay và ngắn gọn khi xuất các giá trị có thể so sánh trong khối) ...

irb(main):001:0> h = { a: 0, b: 5, c: 3, d: 2, e: 3, f:1 }
=> {:a=>0, :b=>5, :c=>3, :d=>2, :e=>3, :f=>1}
irb(main):002:0> h.sort_by { |pair| pair[1] }.to_h
=> {:a=>0, :f=>1, :d=>2, :c=>3, :e=>3, :b=>5}

... hoặc biến thể ngược lại ...

irb(main):003:0> h.sort_by { |pair| pair[1] }.reverse.to_h
=> {:b=>5, :e=>3, :c=>3, :d=>2, :f=>1, :a=>0}

... cũng có tùy chọn để sử dụng Array#sortphương pháp cho phép bạn xác định quy tắc so sánh của riêng mình (ví dụ: điều này sắp xếp theo giá trị tăng dần, nhưng sau đó theo khóa giảm dần trên các giá trị bằng nhau):

irb(main):004:0> h.to_a.sort { |one, other| (one[1] == other[1]) ? other[0] <=> one[0] : one[1] <=> other[1] }.to_h
=> {:a=>0, :f=>1, :d=>2, :e=>3, :c=>3, :b=>5}

Tùy chọn cuối cùng này ít ngắn gọn hơn một chút, nhưng có thể linh hoạt hơn (ví dụ: logic tùy chỉnh để xử lý hỗn hợp các loại).

0 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ ruby sorting , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading