Con Backbone.js ho una raccolta impostata con una funzione di confronto. Sta ordinando bene i modelli, ma vorrei invertire l'ordine.
Come posso ordinare i modelli in ordine decrescente anziché crescente?
Con Backbone.js ho una raccolta impostata con una funzione di confronto. Sta ordinando bene i modelli, ma vorrei invertire l'ordine.
Come posso ordinare i modelli in ordine decrescente anziché crescente?
Risposte:
Bene, puoi restituire valori negativi dal comparatore. Se prendiamo, ad esempio, l'esempio dal sito di Backbone e vogliamo invertire l'ordine, sarà simile a questo:
var Chapter = Backbone.Model;
var chapters = new Backbone.Collection;
chapters.comparator = function(chapter) {
return -chapter.get("page"); // Note the minus!
};
chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));
alert(chapters.pluck('title'));
Personalmente, non sono molto soddisfatto di nessuna delle soluzioni fornite qui:
La soluzione moltiplicando per -1 non funziona quando il tipo di ordinamento non è numerico. Sebbene ci siano modi per aggirare questo problema (chiamando Date.getTime()ad esempio) questi casi sono specifici. Vorrei un modo generale per invertire la direzione di qualsiasi tipo, senza doversi preoccupare del tipo specifico di campo da ordinare.
Per la soluzione stringa, la costruzione di una stringa un carattere alla volta sembra un collo di bottiglia delle prestazioni, specialmente quando si utilizzano raccolte di grandi dimensioni (o addirittura stringhe di campi di ordinamento di grandi dimensioni)
Ecco una soluzione che funziona bene per i campi String, Date e Numeric:
In primo luogo, dichiara un comparatore che invertirà il risultato di un risultato di una funzione sortBy, in questo modo:
function reverseSortBy(sortByFunction) {
return function(left, right) {
var l = sortByFunction(left);
var r = sortByFunction(right);
if (l === void 0) return -1;
if (r === void 0) return 1;
return l < r ? 1 : l > r ? -1 : 0;
};
}
Ora, se vuoi invertire la direzione dell'ordinamento, tutto ciò che dobbiamo fare è:
var Chapter = Backbone.Model;
var chapters = new Backbone.Collection;
// Your normal sortBy function
chapters.comparator = function(chapter) {
return chapter.get("title");
};
// If you want to reverse the direction of the sort, apply
// the reverseSortBy function.
// Assuming the reverse flag is kept in a boolean var called reverseDirection
if(reverseDirection) {
chapters.comparator = reverseSortBy(chapters.comparator);
}
chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));
alert(chapters.pluck('title'));
Il motivo per cui funziona è che Backbone.Collection.sortsi comporta in modo diverso se la funzione di ordinamento ha due argomenti. In questo caso si comporta come il comparatore passato a Array.sort. Questa soluzione funziona adattando la funzione sortBy a un argomento in un comparatore a due argomenti e invertendo il risultato.
Il comparatore di raccolte di Backbone.js si basa sul metodo Underscore.js _.sortBy. Il modo in cui viene implementato sortBy finisce per "avvolgere" javascript .sort () in un modo che rende difficile l'ordinamento delle stringhe al contrario. La semplice negazione della stringa finisce per restituire NaN e interrompe l'ordinamento.
Se devi eseguire un ordinamento inverso con le stringhe, come l'ordinamento alfabetico inverso, ecco un modo davvero hacker per farlo:
comparator: function (Model) {
var str = Model.get("name");
str = str.toLowerCase();
str = str.split("");
str = _.map(str, function(letter) {
return String.fromCharCode(-(letter.charCodeAt(0)));
});
return str;
}
Non è affatto carino, ma è un "negatore di stringhe". Se non hai problemi con la modifica dei tipi di oggetti nativi in javascript, potresti rendere il codice più chiaro estraendo il negatore di stringa e aggiungendolo come metodo su String.Prototype. Tuttavia, probabilmente vuoi farlo solo se sai cosa stai facendo, perché la modifica dei tipi di oggetti nativi in javascript può avere conseguenze impreviste.
La mia soluzione era invertire i risultati dopo l'ordinamento.
Per evitare il doppio rendering, prima ordina con silent, quindi attiva 'reset'.
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
-Date.getTime(), possibilmente con qualche hack in più se ritieni che le date nel tuo sistema attraverseranno l'epoca unix. Per alfabetico, guarda la risposta di Andrew sopra.
Modifica la tua funzione di confronto per restituire un valore proporzionale inverso invece di restituire i dati che sei attualmente. Un po 'di codice da: http://documentcloud.github.com/backbone/#Collection-comparator
Esempio:
var Chapter = Backbone.Model;
var chapters = new Backbone.Collection;
/* Method 1: This sorts by page number */
chapters.comparator = function(chapter) {
return chapter.get("page");
};
/* Method 2: This sorts by page number in reverse */
chapters.comparator = function(chapter) {
return -chapter.get("page");
};
chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));
Quanto segue ha funzionato bene per me:
comparator: function(a, b) {
// Optional call if you want case insensitive
name1 = a.get('name').toLowerCase();
name2 = b.get('name').toLowerCase();
if name1 < name2
ret = -1;
else if name1 > name2
ret = 1;
else
ret = 0;
if this.sort_dir === "desc"
ret = -ret
return ret;
}
collection.sort_dir = "asc";
collection.sort(); // returns collection in ascending order
collection.sort_dir = "desc";
collection.sort(); // returns collection in descending order
Ho letto alcuni in cui la funzione di confronto Backbone utilizza o imita la funzione JavaScript Array.prototype.sort (). Ciò significa che utilizza 1, -1 o 0 per decidere l'ordinamento di ciascun modello nella raccolta. Questo si aggiorna costantemente e la raccolta rimane nell'ordine corretto in base al comparatore.
Informazioni su Array.prototype.sort () possono essere trovate qui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort
Molte risposte a questa domanda invertono semplicemente l'ordine appena prima di renderlo alla pagina. Questo non è l'ideale.
Usando l'articolo precedente su Mozilla come guida, sono stato in grado di mantenere la mia raccolta ordinata in ordine inverso utilizzando il seguente codice, quindi eseguiamo semplicemente il rendering della raccolta sulla pagina nell'ordine corrente e possiamo utilizzare un flag (reverseSortDirection) per invertire l'ordine .
//Assuming this or similar will be in your Backbone.Collection.
sortKey: 'id', //The field name of the item in your collection
reverseSortDirection: false,
comparator: function(a, b) {
var sampleDataA = a.get(this.sortKey),
sampleDataB = b.get(this.sortKey);
if (this.reverseSortDirection) {
if (sampleDataA > sampleDataB) { return -1; }
if (sampleDataB > sampleDataA) { return 1; }
return 0;
} else {
if (sampleDataA < sampleDataB) { return -1; }
if (sampleDataB < sampleDataA) { return 1; }
return 0;
}
},
Comparatore tranne i due modelli, in caso di modifiche alla raccolta o al modello la funzione compartor viene eseguita e cambia l'ordine della raccolta.
Ho testato questo approccio con Numbers e Strings e sembra funzionare perfettamente per me.
Spero davvero che questa risposta possa aiutare poiché ho lottato con questo problema molte volte e di solito finisco per usare un rimedio.
sortKeye reverseSortDirectione la chiamata .sort()da qualsiasi opinione che ha una maniglia sulla raccolta. Più semplice di altre risposte con voti alti, per me.
Questo può essere fatto elegantemente sovrascrivendo il metodo sortBy. Ecco un esempio
var SortedCollection = Backbone.Collection.extend({
initialize: function () {
// Default sort field and direction
this.sortField = "name";
this.sortDirection = "ASC";
},
setSortField: function (field, direction) {
this.sortField = field;
this.sortDirection = direction;
},
comparator: function (m) {
return m.get(this.sortField);
},
// Overriding sortBy (copied from underscore and just swapping left and right for reverse sort)
sortBy: function (iterator, context) {
var obj = this.models,
direction = this.sortDirection;
return _.pluck(_.map(obj, function (value, index, list) {
return {
value: value,
index: index,
criteria: iterator.call(context, value, index, list)
};
}).sort(function (left, right) {
// swap a and b for reverse sort
var a = direction === "ASC" ? left.criteria : right.criteria,
b = direction === "ASC" ? right.criteria : left.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index < right.index ? -1 : 1;
}), 'value');
}
});
Quindi puoi usarlo in questo modo:
var collection = new SortedCollection([
{ name: "Ida", age: 26 },
{ name: "Tim", age: 5 },
{ name: "Rob", age: 55 }
]);
//sort by "age" asc
collection.setSortField("age", "ASC");
collection.sort();
//sort by "age" desc
collection.setSortField("age", "DESC");
collection.sort();
Questa soluzione non dipende dal tipo di campo.
Ecco una soluzione davvero semplice, per chi vuole semplicemente capovolgere l'ordine corrente. È utile se hai una tabella le cui colonne possono essere ordinate in due direzioni.
collection.models = collection.models.reverse()
Puoi combinarlo con qualcosa del genere se sei interessato al case da tavolo (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
Avevo un requisito leggermente diverso, in quanto sto visualizzando i miei modelli in una tabella e volevo essere in grado di ordinarli in base a qualsiasi colonna (proprietà del modello) e ascendente o discendente.
Ci è voluto un bel po 'di confusione per far funzionare tutti i casi (dove i valori possono essere stringhe, stringhe vuote, date, numeri. Tuttavia penso che questo sia abbastanza vicino.
inverseString: function(str)
{
return str.split("").map(function (letter) { return String.fromCharCode(-(letter.charCodeAt(0))); }).join("");
}
getComparisonValue: function (val, desc)
{
var v = 0;
// Is a string
if (typeof val === "string")
{
// Is an empty string, upgrade to a space to avoid strange ordering
if(val.length === 0)
{
val = " ";
return desc ? this.inverseString(val) : val;
}
// Is a (string) representing a number
v = Number(val);
if (!isNaN(v))
{
return desc ? -1 * v : v;
}
// Is a (string) representing a date
v = Date.parse(val);
if (!isNaN(v))
{
return desc ? -1 * v : v;
}
// Is just a string
return desc ? this.inverseString(val) : val;
}
// Not a string
else
{
return desc ? -1 * val : val;
}
},
uso:
comparator: function (item)
{
// Store the collumn to 'sortby' in my global model
var property = app.collections.global.get("sortby");
// Store the direction of the sorting also in the global model
var desc = app.collections.global.get("direction") === "DESC";
// perform a comparison based on property & direction
return this.getComparisonValue(item.get(property), desc);
},
Ho appena sovrascritto la funzione di ordinamento per invertire l'array dei modelli al termine. Inoltre ho tenuto a bada l'attivazione dell'evento "sort" fino a quando il contrario non è stato completato.
sort : function(options){
options = options || {};
Backbone.Collection.prototype.sort.call(this, {silent:true});
this.models.reverse();
if (!options.silent){
this.trigger('sort', this, options);
}
return this;
},
Recentemente ho riscontrato questo problema e ho deciso di aggiungere un metodo rsort.
Collection = Backbone.Collection.extend({
comparator:function(a, b){
return a>b;
},
rsort:function(){
var comparator = this.comparator;
this.comparator = function(){
return -comparator.apply(this, arguments);
}
this.sort();
this.comparator = comparator;
}
});
Si spera che qualcuno lo trovi utile
Ecco un modo semplice e veloce per ordinare al contrario i modelli di una raccolta utilizzando UnderscoreJS
var reverseCollection = _.sortBy(this.models, function(model) {
return self.indexOf(model) * -1;
});