Con Backbone.js tengo una colección configurada con una función de comparación. Está ordenando muy bien los modelos, pero me gustaría invertir el orden.
¿Cómo puedo ordenar los modelos en orden descendente en lugar de ascender?
Con Backbone.js tengo una colección configurada con una función de comparación. Está ordenando muy bien los modelos, pero me gustaría invertir el orden.
¿Cómo puedo ordenar los modelos en orden descendente en lugar de ascender?
Respuestas:
Bueno, puede devolver valores negativos del comparador. Si tomamos, por ejemplo, el ejemplo del sitio de Backbone y queremos invertir el orden, se verá así:
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, no estoy tan contento con ninguna de las soluciones que se dan aquí:
La solución de multiplicar por -1 no funciona cuando el tipo de ordenación no es numérico. Aunque hay formas de evitar esto (llamando, Date.getTime()
por ejemplo), estos casos son específicos. Me gustaría una forma general de invertir la dirección de cualquier tipo, sin tener que preocuparme por el tipo específico de campo que se está ordenando.
Para la solución de cadenas, construir una cadena de un carácter a la vez parece un cuello de botella en el rendimiento, especialmente cuando se utilizan colecciones grandes (o de hecho, cadenas de campos de clasificación grandes)
Aquí hay una solución que funciona muy bien para los campos de cadena, fecha y numérico:
En primer lugar, declare un comparador que revertirá el resultado de un resultado de una función sortBy, como esta:
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;
};
}
Ahora, si desea invertir la dirección del ordenamiento, todo lo que tenemos que hacer es:
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'));
La razón por la que esto funciona es que se Backbone.Collection.sort
comporta de manera diferente si la función de clasificación tiene dos argumentos. En este caso, se comporta de la misma manera que el comparador al que se pasó Array.sort
. Esta solución funciona adaptando la función sortBy de un solo argumento en un comparador de dos argumentos e invirtiendo el resultado.
El comparador de colecciones de Backbone.js se basa en el método Underscore.js _.sortBy. La forma en que se implementa sortBy termina "envolviendo" javascript .sort () de una manera que dificulta la clasificación de cadenas en reversa. La simple negación de la cadena termina devolviendo NaN y rompe la clasificación.
Si necesita realizar una ordenación inversa con cadenas, como una ordenación alfabética inversa, aquí hay una forma realmente pirata de hacerlo:
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;
}
De ninguna manera es bonito, pero es un "negador de cuerdas". Si no tiene ningún reparo en modificar los tipos de objetos nativos en javascript, puede hacer que su código sea más claro extrayendo el negador de cadenas y agregándolo como método en String.Prototype. Sin embargo, probablemente solo desee hacer esto si sabe lo que está haciendo, porque la modificación de los tipos de objetos nativos en javascript puede tener consecuencias imprevistas.
Mi solución fue revertir los resultados después de ordenarlos.
Para evitar el doble renderizado, primero ordene con silencioso, luego active 'reset'.
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
-Date.getTime()
, posiblemente con alguna piratería adicional si cree que las fechas en su sistema cruzarán la época de Unix. Por orden alfabético, mire la respuesta de Andrew anterior.
Modifique su función de comparación para devolver un valor proporcional inverso en lugar de devolver los datos que está actualmente. Algún código de: http://documentcloud.github.com/backbone/#Collection-comparator
Ejemplo:
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"}));
Lo siguiente funcionó bien para mí:
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
Leí en algún lugar que la función del comparador Backbone utiliza o imita la función JavaScript Array.prototype.sort (). Esto significa que utiliza 1, -1 o 0 para decidir el orden de clasificación de cada modelo de la colección. Esto se actualiza constantemente y la colección se mantiene en el orden correcto según el comparador.
Puede encontrar información sobre Array.prototype.sort () aquí: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort
Muchas respuestas a esta pregunta simplemente invierten el orden justo antes de volver a abrirla. Esto no es ideal.
Usando el artículo anterior sobre Mozilla como guía, pude mantener mi colección ordenada en orden inverso usando el siguiente código, luego simplemente representamos la colección en la página en su orden actual y podemos usar una bandera (reverseSortDirection) para invertir el orden .
//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;
}
},
Comparador excepto los dos modelos, al cambiar la colección o modelo, la función de compartimiento se ejecuta y cambia el orden de la colección.
Probé este enfoque con Numbers and Strings y parece funcionar perfectamente para mí.
Realmente espero que esta respuesta pueda ayudar, ya que he luchado con este problema muchas veces y, por lo general, termino usando una solución alternativa.
sortKey
y reverseSortDirection
y llamar .sort()
desde cualquier vista que tenga un identificador en la colección. Para mí, es más simple que otras respuestas con más votos.
Esto se puede hacer con elegancia anulando el método sortBy. Aquí hay un ejemplo
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');
}
});
Entonces puedes usarlo así:
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();
Esta solución no depende del tipo de campo.
Aquí hay una solución realmente simple, para aquellos que simplemente quieren cambiar el orden actual. Es útil si tiene una tabla cuyas columnas se pueden ordenar en dos direcciones.
collection.models = collection.models.reverse()
Puede combinarlo con algo como esto si está interesado en el caso de la tabla (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
Tenía un requisito ligeramente diferente, ya que estoy mostrando mis modelos en una tabla y quería poder ordenarlos por cualquier columna (propiedad del modelo) y ya sea ascendente o descendente.
Se ha necesitado una buena cantidad de trabajo para que todos los casos funcionen (donde los valores pueden ser cadenas, cadenas vacías, fechas, números. Sin embargo, creo que esto está bastante cerca.
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;
}
},
utilizar:
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);
},
Simplemente anulé la función de clasificación para revertir la matriz de modelos al finalizar. También detuve la activación del evento 'sort' hasta que se haya completado el proceso inverso.
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;
},
Recientemente me encontré con este problema y simplemente decidí agregar un método 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;
}
});
Ojalá alguien lo encuentre útil.
Aquí hay una manera rápida y fácil de ordenar de forma inversa los modelos de una colección usando UnderscoreJS
var reverseCollection = _.sortBy(this.models, function(model) {
return self.indexOf(model) * -1;
});