Domanda

Ho una raccolta di oggetti Post e voglio poterli ordinare in base a queste condizioni:

  • Innanzitutto per categoria (news, eventi, laboratori, portfolio, ecc.)
  • Quindi per data, se data, o per posizione, se è stato impostato un indice specifico

Alcuni post avranno date (notizie ed eventi), altri avranno posizioni esplicite (laboratori e portfolio).

Voglio poter chiamare posts.sort!, quindi ho sovrascritto <=>, ma sto cercando il modo più efficace per ordinare in base a queste condizioni.Di seguito è riportato uno pseudo metodo:

def <=>(other)
  # first, everything is sorted into 
  # smaller chunks by category
  self.category <=> other.category

  # then, per category, by date or position
  if self.date and other.date
    self.date <=> other.date
  else
    self.position <=> other.position
  end
end

Sembra che dovrei effettivamente ordinare due volte separate, piuttosto che stipare tutto in un unico metodo.Qualcosa di simile a sort_by_category, Poi sort!.Qual è il modo più rubino per farlo?

È stato utile?

Soluzione

Dovresti sempre ordinare in base agli stessi criteri per garantire un ordine significativo.Se si confrontano due nil date, va bene che il position giudicherà l'ordine, ma se ne confrontiamo uno nil date con una data fissa, devi decidere quale inizia per prima, indipendentemente dalla posizione (ad esempio mappando nil ad un giorno lontano nel passato).

Altrimenti immagina quanto segue:

a.date = nil                   ; a.position = 1
b.date = Time.now - 1.day      ; b.position = 2
c.date = Time.now              ; c.position = 0

Secondo i tuoi criteri originali, avresti:un < b < c < a.Allora, qual è il più piccolo??

Anche tu vuoi fare l'ordinamento subito.Per il tuo <=> implementazione, utilizzo #nonzero?:

def <=>(other)
  return nil unless other.is_a?(Post)
  (self.category <=> other.category).nonzero? ||
  ((self.date || AGES_AGO) <=> (other.date || AGES_AGO)).nonzero? ||
  (self.position <=> other.position).nonzero? ||
  0
end

Se utilizzi i criteri di confronto solo una volta o se tali criteri non sono universali e quindi non vuoi definirli <=>, potresti usare sort con un blocco:

post_ary.sort{|a, b| (a.category <=> ...).non_zero? || ... }

Meglio ancora, c'è sort_by E sort_by! che puoi utilizzare per creare un array per cosa confrontare in quale priorità:

post_ary.sort_by{|a| [a.category, a.date || AGES_AGO, a.position] }

Oltre ad essere più breve, utilizzando sort_by ha il vantaggio che si possono ottenere solo criteri ben ordinati.

Appunti:

  • sort_by! è stato introdotto in Ruby 1.9.2.Puoi require 'backports/1.9.2/array/sort_by' per usarlo con i rubini più vecchi.
  • Lo presumo Post non è una sottoclasse di ActiveRecord::Base (nel qual caso vorresti che l'ordinamento fosse eseguito dal server db).

Altri suggerimenti

In alternativa potresti eseguire l'ordinamento in un colpo solo in un array, l'unico problema è gestire il caso in cui uno degli attributi è nil, anche se ciò potrebbe comunque essere gestito se conoscessi il set di dati selezionando la guardia nil appropriata.Inoltre non è chiaro dal tuo pseudocodice se i confronti di data e posizione sono elencati in un ordine di priorità o nell'uno o nell'altro (ad es.utilizzare la data se esiste per entrambi, altrimenti utilizzare la posizione).La prima soluzione presuppone uso, categoria, seguito dalla data, seguito dalla posizione

def <=>(other)
    [self.category, self.date, self.position] <=> [other.category, other.date, other.position]
end

Il secondo presuppone la data o la posizione

def <=>(other)
    if self.date && other.date
        [self.category, self.date] <=> [other.category, other.date]
    else
        [self.category, self.position] <=> [other.category, other.position]
    end
end
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top