Com Backbone.js , tenho uma coleção configurada com uma função de comparação. É uma boa classificação dos modelos, mas gostaria de inverter a ordem.
Como posso classificar os modelos em ordem decrescente em vez de crescente?
Com Backbone.js , tenho uma coleção configurada com uma função de comparação. É uma boa classificação dos modelos, mas gostaria de inverter a ordem.
Como posso classificar os modelos em ordem decrescente em vez de crescente?
Respostas:
Bem, você pode retornar valores negativos do comparador. Se pegarmos, por exemplo, o exemplo do site do Backbone e quisermos inverter a ordem, ficará assim:
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'));
Pessoalmente, não estou muito satisfeito com nenhuma das soluções fornecidas aqui:
A solução de multiplicação por -1 não funciona quando o tipo de classificação não é numérico. Embora existam maneiras de contornar isso (ligando, Date.getTime()
por exemplo), esses casos são específicos. Gostaria de uma maneira geral de inverter a direção de qualquer tipo, sem ter que me preocupar com o tipo específico de campo que está sendo classificado.
Para a solução de string, construir uma string com um caractere por vez parece um gargalo de desempenho, especialmente ao usar grandes coleções (ou mesmo, grandes strings de campo de classificação)
Esta é uma solução que funciona bem para campos String, Date e Numérico:
Em primeiro lugar, declare um comparador que reverterá o resultado de um resultado de uma função sortBy, assim:
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;
};
}
Agora, se você quiser inverter a direção da classificação, tudo o que precisamos fazer é:
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'));
O motivo pelo qual isso funciona é que Backbone.Collection.sort
se comporta de maneira diferente se a função de classificação tiver dois argumentos. Nesse caso, ele se comporta da mesma maneira que o comparador passou para Array.sort
. Essa solução funciona adaptando a função sortBy de argumento único em um comparador de dois argumentos e revertendo o resultado.
O comparador de coleção do Backbone.js conta com o método _.sortBy de Underscore.js. A forma como o sortBy é implementado acaba "envolvendo" o javascript .sort () de uma forma que torna difícil a classificação reversa das strings. A simples negação da string acaba retornando NaN e quebra a classificação.
Se você precisar realizar uma classificação reversa com Strings, como uma classificação alfabética reversa, aqui está uma maneira realmente hack de fazer isso:
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;
}
Não é nada bonito, mas é um "negador de strings". Se você não tiver nenhum escrúpulo em modificar os tipos de objeto nativos em javascript, poderá tornar seu código mais claro extraindo o negador de string e adicionando-o como um método em String.Prototype. No entanto, você provavelmente só deseja fazer isso se souber o que está fazendo, porque modificar os tipos de objetos nativos em javascript pode ter consequências imprevistas.
Minha solução foi reverter os resultados após a classificação.
Para evitar a renderização dupla, primeiro classifique com silencioso e, em seguida, acione 'reset'.
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
-Date.getTime()
, possivelmente com algum hacking extra se você acredita que as datas em seu sistema irão cruzar a época unix. Para a ordem alfabética, veja a resposta de Andrew acima.
Modifique sua função de comparador para retornar algum valor proporcional inverso em vez de retornar os dados que você está atualmente. Algum código de: http://documentcloud.github.com/backbone/#Collection-comparator
Exemplo:
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"}));
O seguinte funcionou bem para mim:
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
Eu li alguns em que a função de comparador de Backbone usa ou imita a função JavaScript Array.prototype.sort (). Isso significa que ele usa 1, -1 ou 0 para decidir a ordem de classificação de cada modelo na coleção. Isso é atualizado constantemente e a coleção permanece na ordem correta com base no comparador.
Informações sobre Array.prototype.sort () podem ser encontradas aqui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort
Muitas respostas a essa pergunta simplesmente invertem a ordem antes de enviá-la para a página. Isso não é ideal.
Usando o artigo acima no Mozilla como guia, consegui manter minha coleção classificada em ordem reversa usando o código a seguir, então simplesmente renderizamos a coleção na página em sua ordem atual e podemos usar um sinalizador (reverseSortDirection) para inverter a ordem .
//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 exceto dois modelos, nas alterações na coleção ou no modelo, a função de compartimento executa e altera a ordem da coleção.
Eu testei essa abordagem com Numbers e Strings e parece estar funcionando perfeitamente para mim.
Eu realmente espero que esta resposta possa ajudar, já que lutei com esse problema muitas vezes e geralmente acabo usando uma solução alternativa.
sortKey
e reverseSortDirection
e chamada .sort()
a partir de qualquer ponto de vista que tem uma alça sobre a coleção. Mais simples do que outras respostas com maior votação, para mim.
Isso pode ser feito elegantemente substituindo o método sortBy. Aqui está um exemplo
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');
}
});
Então você pode usá-lo assim:
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 solução não depende do tipo de campo.
Aqui está uma solução realmente simples, para aqueles que simplesmente desejam inverter o pedido atual. É útil se você tiver uma tabela cujas colunas podem ser ordenadas em duas direções.
collection.models = collection.models.reverse()
Você pode combiná-lo com algo assim se estiver interessado no caso da mesa (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
Eu tinha um requisito um pouco diferente, no sentido de que estou exibindo meus modelos em uma tabela e queria poder classificá-los por qualquer coluna (propriedade do modelo) e ascendente ou descendente.
Demorou bastante para fazer todos os casos funcionarem (onde os valores podem ser strings, strings vazias, datas, números. No entanto, acho que isso está bem próximo.
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;
}
},
usar:
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);
},
Acabei de substituir a função de classificação para reverter a matriz de modelos após a conclusão. Também evitei o acionamento do evento 'sort' até que o reverso fosse concluído.
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 tive esse problema e decidi adicionar um 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;
}
});
Espero que alguém ache isso útil
Esta é uma maneira rápida e fácil de reverter a classificação dos modelos de uma coleção usando UnderscoreJS
var reverseCollection = _.sortBy(this.models, function(model) {
return self.indexOf(model) * -1;
});