2008-09-16 8 views
18

Realmente me gustaría manejar esto sin parches de mono, pero no he podido encontrar otra opción todavía.¿Cómo puedo ordenar por varias condiciones con diferentes pedidos?

Tengo una matriz (en Ruby) que debo ordenar por varias condiciones. Sé cómo usar el método de clasificación y he usado el truco para ordenar usando una variedad de opciones para ordenar por múltiples condiciones. Sin embargo, en este caso necesito la primera condición para clasificar ascendente y la segunda para clasificar descendente. Por ejemplo:

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

¿Alguna sugerencia?

Edición: Acabo de dar cuenta de que debo mencionar que no puedo comparar fácilmente los valores primero y segundo (en realidad, estoy trabajando con atributos de objeto aquí). Así que para un ejemplo sencillo es más como:

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

su ejemplo modificado se puede tratar de manera idéntica al primero que publicó. El operador <=> trabajará en cualquier objeto de la misma manera (en su caso, los objetos Entero y Cadena se pueden comparar con <=> bien) –

+0

Cierto, solo pensé que debería mencionarlo en lugar de arriesgarme por simplificar el problema. –

Respuesta

32

¿Qué tal:

 

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

¡Maravilloso! Debería haber pensado en eso, ¡sabía que me faltaba algo! ¡Gracias! –

+1

¡Eso es RAD! ¡Es hora de archivar eso en mi libro de trucos de rubíes! ¡Prestigio! –

+4

Encuentro 'a [0] <=> b [0] o b [1] <=> a [1]' un poco más legible. – maasha

4

Tuve este mismo problema básico, y lo resolvió mediante la adición de esto:

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 

Esta es una contenedor que simplemente invierte la función < =>, que luego le permite hacer cosas como esta:

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

Enumerable#multisort es una solución genérica que se puede aplicar a matrices de cualquier tamaño, no solo aquellos con 2 elementos. Los argumentos son booleanos que indican si un campo específico debe ser clasificado de forma ascendente o descendente (abajo): el uso

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

¡Aseado! Gracias, eso sin duda será útil más tarde. –

+0

Guau - el bucle de una sola línea probaría el 99% de los rubyistas que hay. Muy compacto. – drudru

2

He estado usando la receta de Glenn durante bastante tiempo ahora. Cansado de copiar el código de un proyecto a una y otra vez, he decidido que sea una joya:

http://github.com/dadooda/invert

+0

¡Oye, eso es genial! –

7

que estaba teniendo una pesadilla de un tiempo tratando de encontrar la manera de revertir una especie específica atributo, pero normalmente ordenar los otros dos. Solo una nota sobre la clasificación para aquellos que vienen después de esto y se confunden con | a, b | sintaxis de bloque No puede usar el estilo de bloque {|a,b| a.blah <=> b.blah} con sort_by! o sort_by. Debe usarse con sort! o sort. Además, como se indicó anteriormente en los otros carteles, cambie a y b en el operador de comparación <=> para invertir el orden de clasificación. De esta manera:

para ordenar por bla y buche normalmente, pero ordenar por bleu en orden inverso a hacer esto:

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

También es posible utilizar el signo - con sort_by o sort_by! hacer una especie inversa en números (hasta donde yo sé, solo funciona en números así que no lo intentes con cadenas ya que solo comete errores y mata la página).

Suponer a.craw es un número entero.Por ejemplo:

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