2009-04-30 13 views
30

Tengo una matriz de objetos que necesito ordenar por un atributo de posición que podría ser un entero o nulo, y necesito los objetos que tienen la posición nula para estar al final de la matriz. Ahora, puedo forzar a la posición a devolver algún valor en lugar de cero para que array.sort no falle, pero si uso 0 como este valor predeterminado, coloca esos objetos al frente del ordenamiento. ¿Cuál es la mejor manera de hacer este tipo? ¿Debería simplemente establecer los valores nulos en algún número ridículamente alto que esté "casi" siempre garantizado al final? o ¿hay alguna otra manera en que podría causar que el método array.sort coloque los objetos con atributos nil al final de la matriz? el código es el siguiente:ordenando una matriz de objetos ruby ​​por un atributo que podría ser nulo

class Parent 
    def sorted_children 
    children.sort{|a, b| a.position <=> b.position} 
    end 
end 

class Child 
    def position 
    category ? category.position : #what should the else be?? 
    end 
end 

Ahora, si hago el algo 'otro' como 1000000000, entonces es más probable que va a poner en el final de la matriz, pero no me gusta esta solución, ya que es arbitraria

Respuesta

14

¿Y en Child definir <=> basarse en category.position si existe category y artículos de clasificación sin category como siempre mayor que aquellos con un category?

class Child 
    # Not strictly necessary, but will define other comparisons based on <=> 
    include Comparable 
    def <=> other 
    return 0 if !category && !other.category 
    return 1 if !category 
    return -1 if !other.category 
    category.position <=> other.category.position 
    end 
end 

Luego, en Parent sólo puede llamar children.sort.

+3

Sería algo dudoso en esto, ya que limita su capacidad para ordenar cualquier otra cosa sin ser inconsistente. Dicho esto, si la posición es el orden natural de los padres, entonces tiene sentido razonable hacerlo de esta manera. – RHSeeger

+0

@RHSeeger Si comprueba el historial de edición, originalmente había agregado una advertencia sobre qué hacer si necesita ordenar por diferentes criterios, pero estaba redactado de forma confusa y no creo que haya agregado demasiado. Básicamente, puede agregar otros métodos de comparación que tengan el nombre que desee, y si necesita ordenarlos, hágalo en un bloque como el ejemplo original. –

1

no he hecho Rubí desde hace tiempo, pero se puede dividir la hipótesis nula de comprobación de la clasificación (y simplemente permita que el niño # posición para volver nulo):

def sorted_children 
    children.reject{|c| c.position.nil?}.sort_by(&:position) + 
    children.select{|c| c.position.nil?} 
end 

es cierto que no es el más solución eficiente, pero no tiene ningún número mágico.

+0

es superfluo ya que c.position es verdadero si f c.position no es nulo, pero puedes dejarlo para legibilidad. ¡Me gusta! – glenra

+0

Supongo que es cierto en este caso, ya que "posición" es probablemente un número, pero dado que Ruby tiene 2 valores "falsos" (nulos y falsos), intento no asumir que no-nil significa verdadero. Me he mordido por eso antes. :-) – Ken

6

Para ser justos, no estoy muy familiarizado con Ruby, así que tome esto como una idea de algoritmo más que como un código ... y vuelva a escribir el operador: como lo que sea que Ruby sea más limpio.

¿No puedes comprobar nula en la comparación:

class Parent 
    def sorted_children 
    children.sort{|a,b|(a and b) ? a <=> b : (a ? -1 : 1) } 
    end 
end 

Editado para utilizar el código de Glenra, que implementa el mismo que el mío, pero en una cantidad más pequeña (y probablemente más fácil de leer) de código .

88

Me gustaría modificar su tipo para poner nil artículos al final. Prueba algo como esto.

foo = [nil, -3, 100, 4, 6, nil, 4, nil, 23] 

foo.sort { |a,b| a && b ? a <=> b : a ? -1 : 1 } 

=> [-3, 4, 4, 6, 23, 100, nil, nil, nil] 

que dice: si a y b son tanto no nula clasificarlos normalmente, pero si uno de ellos es nulo, devuelve un estado que ordena que uno más grande.

+2

¡Gracias! Esta debería ser la respuesta aceptada. –

+3

De acuerdo, esta debería ser la respuesta aceptada. Si está contento de confiar en los valores de falsedad y falsedad, puede obtener una fracción más corta: 'foo.sort {| a, b | a <=> b || (b && 1) || -1} ' –

+0

No quería anular el operador de la nave espacial (!) En todo el modelo, solo en una operación particular, así que esto es mucho mejor para mi necesidad. – gfd

13

que manejar este tipo de cosas como esta:

children.sort_by {|child| [child.position ? 0 : 1,child.position || 0]} 
+0

¿Puedes explicar esto un poco más? Parece que estás usando 0 como número mágico cuando la posición es nula, lo que puede no ser válido ya que la posición no está definida como mayor a 0 (es una suposición razonable, pero no necesariamente es el caso). – RHSeeger

+1

No, el 0 al final es solo para evitar llamar al <=> en cero. El primer elemento de la matriz ya asegura que todos los nils vendrán después de todas las posiciones válidas. El segundo elemento subsorts entre las cosas con posiciones. Podrías poner algo allí para sustituir a los que tienen nils, pero el ejemplo no requería nada, así que acabo de usar 0. Podría ser 42 o "wombat", no importa. –

+0

Ah, vale, la confusión se debió a mi falta de conocimiento de Ruby. ¿Entiendo correctamente que estás creando una matriz/lista de dos valores, 1 y el valor de si el valor es nulo ... o 0 y el valor si el valor es no nulo ... luego ordenando usando ese par como "el valor para ordenar"? Idea ingeniosa, simplemente no estaba claro para mí porque no reconocí la sintaxis. – RHSeeger

1

Usted puede hacer esto sin anular el operador de la nave espacial mediante la definición de un nuevo método de comparación.

class Child 
    include Comparable 
    def compare_by_category(other) 
    return 0 if !category && !other.category 
    return 1 if !category 
    return -1 if !other.category 
    category.position <=> other.category.position 
    end 
end 

El método sort puede tomar un bloque, por lo que puede a continuación, ordenar el uso de este nuevo método: "? .nil"

children.sort {|a,b| a.compare_by_category(b) } 
+0

gracias BRO, útil +1 – rusllonrails

Cuestiones relacionadas