javascript - Revertir el orden de clasificación con Backbone.js
(15)
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 vez de subir?
Acabo de anular la función de clasificación para invertir la matriz de modelos al finalizar. También evité activar el evento ''ordenar'' hasta que se haya completado el reverso.
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;
},
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;
});
Aquí hay una solución realmente simple, para aquellos que simplemente quieran 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 mesa (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
Bueno, puedes 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''));
El comparador de colecciones 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 el género.
Si necesita realizar una ordenación inversa con cadenas, como orden alfabético inverso, aquí hay una manera realmente hackish 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 el código sea más claro extrayendo el negativo de cadena y agregándolo como un método en String.Prototype. Sin embargo, probablemente solo quiera hacer esto si sabe lo que está haciendo, porque modificar los tipos de objetos nativos en javascript puede tener consecuencias imprevistas.
Esto se puede hacer elegantemente 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.
Leí en algunos casos que la función del comparador Backbone usa o imita la función JavaScript Array.prototype.sort (). Esto significa que usa 1, -1 o 0 para decidir el orden de clasificación de cada modelo en 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 sobre esta pregunta simplemente invierten el orden justo antes de desgarrarlo a la página. 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 renderizamos la colección a 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 dos modelos, sobre los cambios en la colección o modelo que ejecuta la función del compartimiento y cambia el orden de la colección.
He probado 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 un trabajo alternativo.
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
Mi solución fue invertir los resultados después del género.
Para evitar la doble representación primero ordena con el silencio, luego activa ''reiniciar''.
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger(''reset'', collections, {});
Modifique su función de comparación para devolver algún valor proporcionalmente inverso en lugar de devolver los datos que está actualmente. Algunos códigos 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"}));
Para orden inverso en la identificación:
comparator: function(object) { return (this.length - object.id) + 1; }
Personalmente, no estoy tan contento con ninguna de las soluciones dadas aquí:
La solución de multiplicar por -1 no funciona cuando el tipo de clasificación no es numérico. Aunque hay formas de
Date.getTime()
llamando aDate.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 del campo que se está ordenando.Para la solución de cadena, la construcción de una cadena de caracteres uno a la vez parece un cuello de botella de rendimiento, especialmente cuando se utilizan colecciones grandes (o incluso cadenas de campo de clasificación grandes)
Aquí hay una solución que funciona muy bien para los campos 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 quiere invertir la dirección del orden, 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 Backbone.Collection.sort
comporta de manera diferente si la función de ordenación tiene dos argumentos. En este caso, se comporta de la misma manera que el comparador pasó a 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.
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;
}
});
Con suerte, alguien encontrará esto útil
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 subir o bajar.
Se ha necesitado bastante dinero para que todos los casos funcionen (donde los valores pueden ser cadenas, cadenas vacías, fechas, números. Sin embargo, creo que esto es bastante cercano.
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);
},
// For numbers, dates, and other number-like things
var things = new Backbone.Collection;
things.comparator = function (a, b) { return b - a };
// For strings
things.comparator = function (a, b) {
if (a === b) return 0;
return a < b ? -1 : 1;
};