W Backbone.js mam zestaw skonfigurowany z funkcją komparatora. Ładnie sortuje modele, ale chciałbym odwrócić kolejność.
Jak mogę sortować modele w porządku malejącym, a nie rosnącym?
W Backbone.js mam zestaw skonfigurowany z funkcją komparatora. Ładnie sortuje modele, ale chciałbym odwrócić kolejność.
Jak mogę sortować modele w porządku malejącym, a nie rosnącym?
Odpowiedzi:
Cóż, możesz zwrócić wartości ujemne z komparatora. Jeśli weźmiemy na przykład przykład ze strony Backbone i będziemy chcieli odwrócić kolejność, będzie to wyglądać tak:
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'));
Osobiście nie jestem zadowolony z żadnego z podanych tutaj rozwiązań:
Rozwiązanie mnożenia przez -1 nie działa, gdy typ sortowania jest nieliczbowy. Chociaż istnieją sposoby obejścia tego ( Date.getTime()na przykład dzwoniąc ), te przypadki są specyficzne. Chciałbym mieć ogólny sposób na odwrócenie kierunku dowolnego rodzaju, bez martwienia się o konkretny typ sortowanego pola.
W przypadku rozwiązania w postaci ciągów tworzenie ciągu znaków po jednym znaku na raz wydaje się wąskim gardłem wydajności, szczególnie w przypadku korzystania z dużych kolekcji (lub rzeczywiście, dużych ciągów pól sortowania)
Oto rozwiązanie, które działa dobrze w przypadku pól typu String, Date i Numeric:
Najpierw zadeklaruj komparator, który odwróci wynik wyniku funkcji sortBy, na przykład:
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;
};
}
Teraz, jeśli chcesz odwrócić kierunek tego rodzaju, wszystko, co musimy zrobić, to:
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'));
Powodem tego jest to, że Backbone.Collection.sortzachowuje się inaczej, jeśli funkcja sortowania ma dwa argumenty. W tym przypadku zachowuje się tak samo, jak przeszedł komparator Array.sort. To rozwiązanie działa poprzez dostosowanie funkcji sortBy z jednym argumentem do komparatora z dwoma argumentami i odwrócenie wyniku.
Komparator kolekcji Backbone.js opiera się na metodzie Underscore.js _.sortBy. Sposób implementacji sortBy kończy się „zawijaniem” javascript .sort () w sposób, który utrudnia odwrotne sortowanie ciągów. Prosta negacja łańcucha kończy się zwróceniem NaN i zerwaniem sortowania.
Jeśli chcesz wykonać odwrotne sortowanie za pomocą ciągów, na przykład odwrotne sortowanie alfabetyczne, oto naprawdę hakerski sposób:
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;
}
W żadnym wypadku nie jest ładny, ale jest to „negator stringów”. Jeśli nie masz żadnych problemów z modyfikowaniem natywnych typów obiektów w javascript, możesz uczynić kod bardziej przejrzystym, wyodrębniając negator ciągu i dodając go jako metodę w String.Prototype. Jednak prawdopodobnie chcesz to zrobić tylko wtedy, gdy wiesz, co robisz, ponieważ modyfikowanie natywnych typów obiektów w javascript może mieć nieprzewidziane konsekwencje.
Moim rozwiązaniem było odwrócenie wyników po sortowaniu.
Aby zapobiec podwójnemu renderowaniu, najpierw posortuj z wyciszeniem, a następnie uruchom „reset”.
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
-Date.getTime(), być może z dodatkowym hakowaniem, jeśli uważasz, że daty w twoim systemie przekroczą epokę uniksową. Jeśli chodzi o alfabet, spójrz na odpowiedź Andrzeja powyżej.
Zmodyfikuj funkcję komparatora, aby zamiast zwracać aktualne dane, zwracała pewną odwrotnie proporcjonalną wartość. Jakiś kod z: http://documentcloud.github.com/backbone/#Collection-comparator
Przykład:
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"}));
Poniższe działały dobrze dla mnie:
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
Czytałem, że funkcja komparatora Backbone wykorzystuje lub naśladuje funkcję JavaScript Array.prototype.sort (). Oznacza to, że używa 1, -1 lub 0, aby zdecydować o kolejności sortowania każdego modelu w kolekcji. Jest to stale aktualizowane, a kolekcja pozostaje we właściwej kolejności na podstawie komparatora.
Informacje o Array.prototype.sort () można znaleźć tutaj: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort
Wiele odpowiedzi na to pytanie po prostu odwraca kolejność tuż przed wyświetleniem go na stronie. To nie jest idealne.
Korzystając z powyższego artykułu o Mozilli jako przewodnika, byłem w stanie posortować moją kolekcję w odwrotnej kolejności za pomocą następującego kodu, następnie po prostu renderujemy kolekcję na stronę w jej bieżącej kolejności i możemy użyć flagi (reverseSortDirection) do odwrócenia kolejności .
//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;
}
},
Komparator z wyjątkiem dwóch modeli, po zmianach w kolekcji lub modelu działa funkcja compartor i zmienia kolejność kolekcji.
Przetestowałem to podejście z liczbami i ciągami i wydaje się, że działa idealnie dla mnie.
Naprawdę mam nadzieję, że ta odpowiedź może pomóc, ponieważ wiele razy zmagałem się z tym problemem i zwykle używam obejścia.
sortKeyi reverseSortDirectioni połączenia .sort()z dowolnym widoku, który ma uchwyt na zbiorach. Dla mnie prostsze niż inne odpowiedzi, które uzyskały wyższą liczbę głosów.
Można to zrobić elegancko, zastępując metodę sortBy. Oto przykład
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');
}
});
Możesz więc używać tego w ten sposób:
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();
To rozwiązanie nie zależy od typu pola.
Oto naprawdę proste rozwiązanie dla tych, którzy chcą po prostu odwrócić obecną kolejność. Jest to przydatne, jeśli masz tabelę, której kolumny można uporządkować w dwóch kierunkach.
collection.models = collection.models.reverse()
Możesz połączyć to z czymś takim, jeśli interesuje Cię etui na stół (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
Miałem nieco inne wymaganie, ponieważ wyświetlam moje modele w tabeli i chciałem mieć możliwość sortowania ich według dowolnej kolumny (właściwości modelu) i rosnąco lub malejąco.
Zajęło sporo czasu, aby wszystkie przypadki działały (gdzie wartościami mogą być łańcuchy, puste łańcuchy, daty, liczby. Jednak myślę, że jest to dość blisko).
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;
}
},
posługiwać się:
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);
},
Po prostu nadpisałem funkcję sortowania, aby odwrócić tablicę modeli po zakończeniu. Ponadto wstrzymałem wywołanie zdarzenia „sort” do czasu zakończenia operacji odwrotnej.
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;
},
Niedawno napotkałem ten problem i po prostu zdecydowałem się dodać metodę 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;
}
});
Mam nadzieję, że ktoś uzna to za przydatne
Oto szybki i łatwy sposób odwrotnego sortowania modeli kolekcji za pomocą UnderscoreJS
var reverseCollection = _.sortBy(this.models, function(model) {
return self.indexOf(model) * -1;
});