10

Ich möchte eine Galerie von products anzeigen, wo ich Produkte, die sowohl zum Verkauf als auch zum Verkauf stehen, enthalten. Nur ich möchte, dass die products, die zum Verkauf stehen, am Anfang der Liste erscheinen und die Objekte, die nicht zum Verkauf stehen, erscheinen am Ende der Liste.Ist es möglich, eine Liste von Objekten abhängig von der Antwort des einzelnen Objekts auf eine Methode zu sortieren?

Eine einfache Möglichkeit für mich, dies zu tun ist zwei Listen zu machen, sie dann zusammenführen (eine Liste der on_sale Objekte und eine Liste der nicht on_sale Objekte?):

available_products = [] 
sold_products = [] 
@products.each do |product| 
    if product.on_sale? 
    available_products << product 
    else 
    sold_products << product 
    end 
end 

. . . Aber, um die Struktur meiner bestehenden App zu tun, würde dies eine übermäßige Menge an Refactoring aufgrund einer Kuriosität in meinem Code erfordern (ich verliere meine Seitennummerierung, und ich würde lieber nicht umgestalten). Es wäre einfacher, wenn es eine Möglichkeit gäbe, die existierende Liste von Objekten nach der product Methode zu sortieren, die einen booleschen Wert zurückgibt.

Ist es möglich, eine vorhandene Liste eleganter zu durchlaufen und sie nach diesem wahren oder falschen Wert in Rails zu sortieren? Ich frage nur, weil es so viel ist, dass ich mich nicht bewusst bin, versteckt in diesem Rahmen/Sprache (Ruby) und ich würde gerne wissen, ob sie vor mir gearbeitet haben.

Antwort

11

Sicher. Im Idealfall würden wir so etwas wie dieses sort_by! mit tun:

@products.sort_by! {|product| product.on_sale?} 

oder die snazzier

@products.sort_by!(&:on_sale?) 

aber leider nicht <=> nicht für booleans arbeiten (siehe Why doesn't sort or the spaceship (flying saucer) operator (<=>) work on booleans in Ruby?) und sort_by nicht für boolean funktioniert Werte, so müssen wir diesen Trick verwenden (dank rohit89!)

@products.sort_by! {|product| product.on_sale? ? 0 : 1} 

Wenn Sie schicker werden wollen, nimmt die sort Methode einen Block, und innerhalb dieses Blocks können Sie jede beliebige Logik verwenden, einschließlich Typumwandlung und mehrere Schlüssel. Probieren Sie etwas wie folgt aus:

@products.sort! do |a,b| 
    a_value = a.on_sale? ? 0 : 1 
    b_value = b.on_sale? ? 0 : 1 
    a_value <=> b_value 
end 

oder dies:

@products.sort! do |a,b| 
    b.on_sale?.to_s <=> a.on_sale?.to_s 
end 

(Putting b vor ein, weil Sie wollen "true" Werte kommen vor "false")

oder wenn Sie eine sekundäre Sortier haben:

@products.sort! do |a,b| 
    if a.on_sale? != b.on_sale? 
    b.on_sale?.to_s <=> a.on_sale?.to_s 
    else 
    a.name <=> b.name 
    end 
end 

Beachten Sie, dass sort einen zurückgibt ew Sammlung, die in der Regel eine sauberere, weniger fehleranfällige Lösung ist, aber sort! ändert den Inhalt der ursprünglichen Sammlung, die Sie sagten, war eine Anforderung.

+0

Sieht gut getan werden! Aber ich habe Probleme mit meinem spezifischen Code, vielleicht kannst du mir dabei helfen. Wenn ich den Einzeiler verwende, bekomme ich den folgenden Fehler "Vergleich von FalseClass mit true failed", und wenn ich den ersten Code verwende, den du erwähnt hast, bekomme ich einen Vergleich von [MyModule] :: Product mit [MyModule] :: Product failed '. Nicht sicher, warum das scheitern würde. – Ecnalyr

+0

Einige Anmerkungen: 1) Man sollte immer 'sort_by' verwenden, wenn möglich (deklarativer, prägnanter, effizienter). Es ist möglich, es in allen oben gezeigten Snippets zu verwenden. 2) Es mag mir besser sein, auf die nicht-destruktive 'Enumerable # sort_by' zu verlinken, in-place-Updates sollten nach Möglichkeit vermieden werden. 3) '@ product.sort_by (&: on_sale?)'. – tokland

+0

Sieht aus wie '<=>' funktioniert nicht richtig für Booleans (in Ruby-1.9.3-P327). Schauen Sie es jetzt ... Ja, http://grosser.it/2010/07/30/ruby-true-false-comparison-with/ bestätigt es für eine frühere Version – AlexChaffee

4

@products.sort_by {|product| product.on_sale? ? 0 : 1}

Dies ist, was ich tat, als ich auf booleans Sortieren musste basiert.

4

Keine Notwendigkeit zu sortieren:

products_grouped = @products.partition(&:on_sale?).flatten(1) 
0

aufsteigende und absteigende kann durch inter-Wechsel "false" und "true"

Products.sort_by {|product| product.on_sale? == true ? "false" : "true" }