2008-09-16 9 views
18

Tôi thực sự muốn xử lý điều này mà không cần vá khỉ nhưng tôi chưa thể tìm thấy một tùy chọn khác.Làm cách nào để sắp xếp theo nhiều điều kiện với các đơn đặt hàng khác nhau?

Tôi có một mảng (trong Ruby) mà tôi cần sắp xếp theo nhiều điều kiện. Tôi biết cách sử dụng phương pháp sắp xếp và tôi đã sử dụng mẹo để sắp xếp bằng cách sử dụng một loạt các tùy chọn để sắp xếp theo nhiều điều kiện. Tuy nhiên, trong trường hợp này tôi cần điều kiện đầu tiên để sắp xếp tăng dần và thứ hai để sắp xếp giảm dần. Ví dụ:

ordered_list = [[1, 2], [1, 1], [2, 1]] 

Mọi đề xuất?

Chỉnh sửa: Chỉ cần nhận ra tôi nên đề cập đến rằng tôi không thể dễ dàng so sánh giá trị đầu tiên và thứ hai (tôi thực sự đang làm việc với các thuộc tính đối tượng ở đây). Vì vậy, đối với một ví dụ đơn giản nó giống như hơn:

ordered_list = [[1, "b"], [1, "a"], [2, "a"]] 
+0

ví dụ đã sửa đổi của bạn có thể được xử lý giống hệt với mẫu đầu tiên bạn đăng. Toán tử <=> sẽ hoạt động trên bất kỳ đối tượng nào giống nhau (trong trường hợp của bạn, các đối tượng Integer và String đều có thể được so sánh với <=> tốt) –

+0

Đúng, tôi nghĩ nó nên đề cập đến nó hơn là rủi ro khi đơn giản hóa vấn đề. –

Trả lời

32

Làm thế nào về:

 

ordered_list = [[1, "b"], [1, "a"], [2, "a"]] 
ordered_list.sort! do |a,b| 
    [a[0],b[1]] <=> [b[0], a[1]] 
end 
 
+0

Tuyệt vời! Nên đã nghĩ về điều đó, biết rằng tôi đã bỏ lỡ một cái gì đó! Cảm ơn! –

+1

Đó là RAD! Thời gian để nộp nó đi trong cuốn sách thủ thuật ruby ​​của tôi! Thanh danh! –

+4

Tôi tìm thấy 'a [0] <=> b [0] hoặc b [1] <=> a [1]' dễ đọc hơn một chút. – maasha

4

Tôi có vấn đề này cơ bản giống nhau, và giải quyết nó bằng cách thêm này:

class Inverter 
    attr_reader :o 

    def initialize(o) 
    @o = o 
    end 

    def <=>(other) 
    if @o.is && other.o.is 
     -(@o <=> other.o) 
    else 
     @o <=> other.o 
    end 
    end 
end 

Đây là một trình bao bọc chỉ đơn giản là chuyển đổi chức năng < =>, sau đó cho phép bạn thực hiện những việc như sau:

your_objects.sort_by {|y| [y.prop1,Inverter.new(y.prop2)]} 
4

Enumerable#multisort là một giải pháp chung có thể áp dụng cho các mảng bất kỳ kích thước, không chỉ với các mục có 2 mục. Đối số là các toán tử cho biết liệu một trường cụ thể có được sắp xếp tăng dần hoặc giảm dần hay không (sử dụng bên dưới):

items = [ 
    [3, "Britney"], 
    [1, "Corin"], 
    [2, "Cody"], 
    [5, "Adam"], 
    [1, "Sally"], 
    [2, "Zack"], 
    [5, "Betty"] 
] 

module Enumerable 
    def multisort(*args) 
    sort do |a, b| 
     i, res = -1, 0 
     res = a[i] <=> b[i] until !res.zero? or (i+=1) == a.size 
     args[i] == false ? -res : res 
    end 
    end 
end 

items.multisort(true, false) 
# => [[1, "Sally"], [1, "Corin"], [2, "Zack"], [2, "Cody"], [3, "Britney"], [5, "Betty"], [5, "Adam"]] 
items.multisort(false, true) 
# => [[5, "Adam"], [5, "Betty"], [3, "Britney"], [2, "Cody"], [2, "Zack"], [1, "Corin"], [1, "Sally"]] 
+0

Gọn gàng! Cảm ơn, điều đó chắc chắn sẽ có ích sau này. –

+0

Wow - vòng lặp đơn sẽ kiểm tra 99% số rubyists ở đó. Rất nhỏ gọn. – drudru

2

Tôi đã sử dụng công thức của Glenn trong một thời gian ngắn. Mệt mỏi vì sao chép mã từ dự án để dự án hơn và hơn nữa, tôi đã quyết định để làm cho nó một viên ngọc:

http://github.com/dadooda/invert

+0

Hey, rất tuyệt! –

7

Tôi đã có một cơn ác mộng của một thời gian cố gắng tìm hiểu làm thế nào để đảo ngược loại một cụ thể thuộc tính nhưng thường phân loại hai thuộc tính kia. Chỉ cần một lưu ý về việc phân loại cho những người đến sau này và bị nhầm lẫn bởi | a, b | cú pháp khối. Bạn không thể sử dụng kiểu dáng {|a,b| a.blah <=> b.blah} kiểu khối với sort_by! hoặc sort_by. Nó phải được sử dụng với sort! hoặc sort. Ngoài ra, như được chỉ ra trước đây bởi các áp phích khác, hoán đổi ab trên toán tử so sánh <=> để đảo ngược thứ tự sắp xếp. Như thế này:

Để sắp xếp bởi blah và bầu diều của chim bình thường, nhưng sắp xếp theo bleu theo thứ tự ngược làm điều này:

something.sort!{|a,b| [a.blah, b.bleu, a.craw] <=> [b.blah, a.bleu, b.craw]} 

Nó cũng có thể sử dụng dấu - với sort_by hoặc sort_by! để làm một loại ngược về số (theo như tôi biết nó chỉ hoạt động trên các con số vì vậy đừng thử nó với các chuỗi vì nó chỉ là lỗi và giết chết trang).

Giả sử a.craw là một số nguyên.Ví dụ:

something.sort_by!{|a| [a.blah, -a.craw, a.bleu]}