ordinamento di una matrice di oggetti rubino per un attributo che potrebbe essere nullo

StackOverflow https://stackoverflow.com/questions/808318

  •  03-07-2019
  •  | 
  •  

Domanda

Ho una matrice di oggetti che devo ordinare per un attributo di posizione che potrebbe essere un numero intero o nullo, e ho bisogno che gli oggetti che hanno la posizione zero siano alla fine della matrice. Ora, posso forzare la posizione a restituire un valore piuttosto che zero in modo che array.sort non fallisca, ma se uso 0 come impostazione predefinita, mette questi oggetti in primo piano nell'ordinamento. Qual è il modo migliore per fare questo tipo? dovrei semplicemente impostare i valori zero su un numero ridicolmente alto che è "quasi" sempre garantito alla fine? o c'è qualche altro modo in cui potrei causare il metodo array.sort di mettere gli oggetti dell'attributo nil alla fine dell'array? il codice è simile al seguente:

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 trasformo l '"altro" in qualcosa di simile a 1000000000, è molto probabile che li metterà alla fine dell'array, ma non mi piace questa soluzione perché è arbitraria

È stato utile?

Soluzione

Che ne dici di Child definire <=> basarsi su category.position se category esiste e ordinare gli oggetti senza un Parent come sempre maggiore di quelli con un children.sort?

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

Quindi in <=> puoi semplicemente chiamare <=>.

Altri suggerimenti

Vorrei solo modificare il tuo tipo per mettere nil gli ultimi elementi. Prova qualcosa del genere.

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]

Detto questo: se aeb sono entrambi non nulli, ordinali normalmente, ma se uno di essi è nullo, restituisci uno stato che ordina quello più grande.

Gestisco questo tipo di cose come questa:

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

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

Non puoi semplicemente controllare lo zero nel confronto:

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

Modificato per usare il codice di Glenra, che implementa la stessa cosa della mia ma in una quantità di codice più piccola (e probabilmente più facile da leggere).

Non ho fatto Ruby da un po ', ma potresti dividere il controllo null dall'ordinamento (e consentire solo alla posizione Child # di restituire null):

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

Certamente non è la soluzione più efficiente, ma non ha numeri magici.

Puoi farlo senza sovrascrivere l'operatore di astronave definendo 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ò richiedere un blocco, quindi puoi ordinarlo usando questo nuovo metodo:

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

La soluzione più semplice per me è

def sorted_children(children)
  children.sort_by { |child| child.position || -1}
end
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top