Avec Backbone.js, j'ai une collection configurée avec une fonction de comparaison. C'est bien le tri des modèles, mais j'aimerais inverser l'ordre.
Comment puis-je trier les modèles par ordre décroissant plutôt que par ordre croissant?
Avec Backbone.js, j'ai une collection configurée avec une fonction de comparaison. C'est bien le tri des modèles, mais j'aimerais inverser l'ordre.
Comment puis-je trier les modèles par ordre décroissant plutôt que par ordre croissant?
Réponses:
Eh bien, vous pouvez renvoyer des valeurs négatives à partir du comparateur. Si nous prenons, par exemple, l'exemple du site de Backbone et que nous voulons inverser l'ordre, cela ressemblera à ceci:
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'));
Personnellement, je ne suis pas très satisfait des solutions proposées ici:
La solution de multiplication par -1 ne fonctionne pas lorsque le type de tri n'est pas numérique. Bien qu'il existe des moyens de contourner cela (en appelant Date.getTime()par exemple), ces cas sont spécifiques. Je voudrais une manière générale d'inverser la direction de toute sorte, sans avoir à me soucier du type spécifique du champ à trier.
Pour la solution de chaîne, la construction d'une chaîne un caractère à la fois semble être un goulot d'étranglement des performances, en particulier lors de l'utilisation de grandes collections (ou même de grandes chaînes de champ de tri)
Voici une solution qui fonctionne bien pour les champs String, Date et Numeric:
Tout d'abord, déclarez un comparateur qui inversera le résultat d'un résultat d'une fonction sortBy, comme ceci:
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;
};
}
Maintenant, si vous voulez inverser le sens du tri, tout ce que nous devons faire est:
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 raison pour laquelle cela fonctionne est qu'il Backbone.Collection.sortse comporte différemment si la fonction de tri a deux arguments. Dans ce cas, il se comporte de la même manière que le comparateur passé Array.sort. Cette solution fonctionne en adaptant la fonction sortBy à un seul argument dans un comparateur à deux arguments et en inversant le résultat.
Le comparateur de collection de Backbone.js repose sur la méthode Underscore.js _.sortBy. La façon dont sortBy est implémenté finit par «envelopper» javascript .sort () d'une manière qui rend difficile le tri des chaînes en sens inverse. Une simple négation de la chaîne finit par renvoyer NaN et rompt le tri.
Si vous devez effectuer un tri inversé avec des chaînes, comme un tri alphabétique inversé, voici une façon vraiment hackish de le faire:
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;
}
Ce n'est en aucun cas joli, mais c'est un "négateur de chaîne". Si vous n'avez aucun scrupule à modifier les types d'objets natifs en javascript, vous pouvez clarifier votre code en extrayant le négateur de chaîne et en l'ajoutant en tant que méthode sur String.Prototype. Cependant, vous ne voudrez probablement le faire que si vous savez ce que vous faites, car la modification de types d'objets natifs en javascript peut avoir des conséquences imprévues.
Ma solution était d'inverser les résultats après le tri.
Pour éviter le double rendu, effectuez d'abord le tri avec silencieux, puis déclenchez 'reset'.
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
-Date.getTime(), éventuellement avec un piratage supplémentaire si vous pensez que les dates de votre système traverseront l'époque Unix. Pour l'ordre alphabétique, regardez la réponse d'Andrew ci-dessus.
Modifiez votre fonction de comparaison pour renvoyer une valeur proportionnelle inversée au lieu de renvoyer les données que vous êtes actuellement. Un peu de code de: http://documentcloud.github.com/backbone/#Collection-comparator
Exemple:
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"}));
Ce qui suit a bien fonctionné pour moi:
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
J'en ai lu où la fonction de comparaison Backbone utilise ou imite la fonction JavaScript Array.prototype.sort (). Cela signifie qu'il utilise 1, -1 ou 0 pour décider de l'ordre de tri de chaque modèle de la collection. Cela se met constamment à jour et la collection reste dans le bon ordre en fonction du comparateur.
Des informations sur Array.prototype.sort () peuvent être trouvées ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort
De nombreuses réponses à cette question inversent simplement l'ordre juste avant de le rendre sur la page. Ce n'est pas idéal.
En utilisant l'article ci-dessus sur Mozilla comme guide, j'ai pu garder ma collection triée dans l'ordre inverse en utilisant le code suivant, puis nous rendons simplement la collection à la page dans son ordre actuel et nous pouvons utiliser un indicateur (reverseSortDirection) pour inverser l'ordre .
//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;
}
},
Comparateur à l'exception des deux modèles, lors des modifications apportées à la collection ou au modèle, la fonction de compartiment s'exécute et change l'ordre de la collection.
J'ai testé cette approche avec Numbers and Strings et cela semble fonctionner parfaitement pour moi.
J'espère vraiment que cette réponse pourra vous aider car j'ai lutté avec ce problème à plusieurs reprises et finit généralement par utiliser un travail de contournement.
sortKeyet reverseSortDirectionet appeler à .sort()partir de n'importe quelle vue qui a une poignée sur la collection. Plus simple que d'autres réponses aux votes plus élevés, pour moi.
Cela peut être fait avec élégance en remplaçant la méthode sortBy. Voici un exemple
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');
}
});
Vous pouvez donc l'utiliser comme ceci:
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();
Cette solution ne dépend pas du type de champ.
Voici une solution vraiment simple, pour ceux qui veulent simplement inverser l'ordre actuel. C'est utile si vous avez une table dont les colonnes peuvent être ordonnées dans deux directions.
collection.models = collection.models.reverse()
Vous pouvez le combiner avec quelque chose comme ceci si vous êtes intéressé par le cas de table (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
J'avais une exigence légèrement différente, en ce que j'affiche mes modèles dans un tableau, et je voulais pouvoir les trier par n'importe quelle colonne (propriété du modèle) et soit par ordre croissant soit par ordre décroissant.
Il a fallu beaucoup de temps pour que tous les cas fonctionnent (où les valeurs peuvent être des chaînes, des chaînes vides, des dates, des nombres. Cependant, je pense que c'est assez proche.
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;
}
},
utilisation:
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);
},
Je viens de remplacer la fonction de tri pour inverser le tableau des modèles à la fin. J'ai également retardé le déclenchement de l'événement «sort» jusqu'à ce que l'inverse soit terminé.
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;
},
Récemment rencontré ce problème et vient de décider d'ajouter une méthode de tri.
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;
}
});
J'espère que quelqu'un trouvera cela utile
Voici un moyen rapide et facile de trier à l'envers les modèles d'une collection à l'aide de UnderscoreJS
var reverseCollection = _.sortBy(this.models, function(model) {
return self.indexOf(model) * -1;
});