2009-04-30 13 views
30

Ho una matrice di oggetti che ho bisogno di ordinare da un attributo di posizione che potrebbe essere un intero o nullo, e ho bisogno degli oggetti che hanno la posizione nil essere alla fine della serie. Ora, posso forzare la posizione a restituire un valore piuttosto che nil in modo che l'array.sort non abbia esito negativo, ma se uso 0 come predefinito, mette gli oggetti in cima all'ordinamento. Qual è il modo migliore per fare questo genere? dovrei semplicemente impostare i valori nulli su un numero incredibilmente alto che è quasi sempre garantito alla fine? o c'è un altro modo in cui potrei causare il metodo array.sort per mettere gli oggetti nil degli attributi alla fine dell'array? il codice è simile al seguente:ordinamento di una matrice ruby ​​di oggetti con un attributo che potrebbe essere nil

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 

ora, se faccio la cosa 'altra' come 1000000000, allora è più probabile intenzione di metterli alla fine della matrice, ma non mi piace questa soluzione in quanto è arbitraria

risposta

14

ne dite in Child definizione <=> basarsi su category.position se category esiste, e gli elementi di ordinamento senza una category come sempre maggiore di quelli 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 

Poi nel Parent si può chiamare children.sort.

+3

Sarei un po 'incerto su questo perché limita la tua capacità di ordinare su qualsiasi altra cosa senza essere incoerente. Detto questo, se la posizione è l'ordinamento naturale per genitore, allora ha un ragionevole senso di farlo in questo modo. – RHSeeger

+0

@RHSeeger Se si controlla la cronologia delle modifiche, in origine avevo aggiunto un avvertimento su cosa fare se fosse necessario ordinare secondo criteri diversi, ma era formulato in modo confuso e non pensavo che aggiungesse molto. Fondamentalmente, puoi aggiungere altri metodi di confronto che sono nominati come vuoi, e se hai bisogno di ordinarli, fallo in un blocco come nell'esempio originale. –

1

non ho fatto Ruby in un po ', ma si potrebbe dividere il nulla-checking dal ordinamento (e solo consentire Bambino # posizione per tornare null):

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

Certo non è il più soluzione efficiente, ma non ha numeri magici.

+0

è superfluo dal momento che c.position è true iff c.position non è nullo, ma potresti lasciarlo in lettura. Mi piace! – glenra

+0

Suppongo che sia vero in questo caso, dal momento che "position" è probabilmente un numero, ma poiché Ruby ha 2 valori "false" (nil e false), cerco di non assumere che non-zero significhi vero. Sono stato morso da quello prima. :-) – Ken

6

Per essere onesti, non ho molta familiarità con Ruby, quindi prendi questo più come idea di algoritmo piuttosto che come codice uno ... e riscrivi l'operatore?: Come qualsiasi cosa Ruby abbia che è più pulito.

Non puoi semplicemente verificare la presenza di zero nel confronto:

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

A cura di utilizzare il codice di Glenra, che implementa la stessa cosa come la mia, ma in un più piccolo (e probabilmente più facile da leggere) quantità di codice .

88

Vorrei semplicemente modificare il tipo per mettere gli elementi nil per ultimo. Prova qualcosa di simile.

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] 

che dice: se a e b sono entrambi non-nil ordinarli normalmente, ma se uno di loro è pari a zero, restituire uno stato che ordina che uno più grande.

+2

Grazie! Questa dovrebbe essere la risposta accettata. –

+3

Concordato, questa dovrebbe essere la risposta accettata. Se sei felice di fare affidamento su valori di verità e falsi, puoi ottenerne una frazione più breve: 'foo.sort {| a, b | a <=> b || (b && 1) || -1} ' –

+0

Non volevo scavalcare l'operatore dell'astronave (!) Sull'intero modello, solo in una particolare operazione, quindi è molto meglio per le mie necessità. – gfd

13

mi occupo di questo genere di cose come questa:

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

Puoi spiegarci un po 'di più? Sembra che tu stia usando 0 come numero magico quando la posizione è zero, il che potrebbe non essere valido poiché la posizione non è definita come maggiore 0 (è un presupposto ragionevole, ma non è necessariamente il caso). – RHSeeger

+1

No, lo 0 alla fine serve solo per evitare di chiamare <=> su zero. Il primo elemento dell'array garantisce già che tutti i nils verranno dopo tutte le posizioni valide. Il secondo elemento sussiste tra le cose con posizioni. Potresti mettere qualcosa lì dentro per sostituire quelli con nils, ma l'esempio non ha richiesto nulla, quindi ho usato solo 0. Potrebbe essere 42 o "wombat", non importa. –

+0

Ah, ok, la confusione era dovuta alla mia mancanza di conoscenza di Ruby. Sto capendo correttamente nel fatto che in realtà stai creando un array/elenco di due valori, 1 e il valore per se il valore è nil ... o 0 e il valore se il valore è non nullo ... quindi l'ordinamento utilizzando quella coppia come "il valore su cui ordinare"? Nifty idea, non mi è stato chiaro perché non ho riconosciuto la sintassi. – RHSeeger

1

Si può fare questo senza sostituire l'operatore astronave attraverso la definizione di un nuovo metodo di confronto.

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 

Il metodo sort può prendere un blocco, in modo da poter quindi ordinare utilizzando questo nuovo metodo: "? .nil"

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

grazie BRO, utile +1 – rusllonrails

Problemi correlati