С Backbone.js У меня есть коллекция, созданная с помощью функции компаратора. Он красиво сортирует модели, но я хотел бы изменить порядок. Как я могу сортировать модели по убыванию, а не по возрастанию?
Обратный порядок сортировки с помощью Backbone.js
Ответ 1
Ну, вы можете вернуть отрицательные значения из компаратора. Если мы возьмем, например, пример с сайта Backbone и хотим изменить порядок, он будет выглядеть следующим образом:
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'));
Ответ 2
Лично я не очень доволен любым из решений, приведенных здесь:
-
Решение multipaling by -1 не работает, если тип сортировки не является числовым. Хотя есть способы обойти это (например, вызывая
Date.getTime()
), эти случаи являются конкретными. Я бы хотел, чтобы общий способ изменить направление любого вида, не беспокоясь о том, какой тип сортируемого поля сортируется. -
Для строкового решения построение строки по одному символу за раз похоже на узкое место производительности, особенно при использовании больших коллекций (или действительно больших строк полей сортировки)
Вот решение, которое прекрасно работает для полей String, Date и Numeric:
Во-первых, объявите компаратор, который изменит результат результата функции sortBy, например:
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;
};
}
Теперь, если вы хотите изменить направление сортировки, все, что нам нужно сделать, это:
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'));
Причина этого в том, что Backbone.Collection.sort
ведет себя по-другому, если функция sort имеет два аргумента. В этом случае он ведет себя так же, как компаратор передается в Array.sort
. Это решение работает, адаптируя функцию sortBy одного аргумента в двухпараметрический анализатор и реверсив результат.
Ответ 3
Компилятор коллекции Backbone.js полагается на метод Underscore.js _.sortBy. Способ sortBy реализуется, завершая "обертывание" javascript.sort() таким образом, что затрудняет сортировку строк в обратном порядке. Простое отрицание строки заканчивается возвратом NaN и разрывает сортировку.
Если вам нужно выполнить обратную сортировку со строками, например, в обратном алфавитном порядке, здесь очень хакерский способ сделать это:
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;
}
Это ни в коем случае не симпатичный, но это "строковый отрицатель". Если у вас нет каких-либо проблем с изменением типов собственных объектов в javascript, вы можете сделать код более понятным, извлекая нулевой символ и добавив его как метод в String.Prototype. Однако вы, вероятно, только хотите это сделать, если знаете, что делаете, потому что изменение типов родных объектов в javascript может иметь непредвиденные последствия.
Ответ 4
Мое решение состояло в том, чтобы отменить результаты после сортировки.
Чтобы избежать двойного рендеринга, сначала выполните сортировку с тихим, затем нажмите "reset".
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
Ответ 5
Измените функцию компаратора, чтобы вернуть некоторое обратно пропорциональное значение вместо того, чтобы возвращать данные, которые вы в настоящее время. Некоторый код из: http://documentcloud.github.com/backbone/#Collection-comparator
Пример:
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"}));
Ответ 6
Для меня работали хорошо:
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
Ответ 7
Я читал некоторые, где функция компаратора Backbone использует или имитирует функцию JavaScript Array.prototype.sort(). Это означает, что он использует либо 1, -1, либо 0, чтобы определить порядок сортировки каждой модели в коллекции. Это постоянно обновляется, и коллекция остается в правильном порядке на основе компаратора.
Информацию о Array.prototype.sort() можно найти здесь: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2Fsort
Многие ответы по этому вопросу просто отменяют порядок, прежде чем разорвать его на странице. Это не идеально.
Используя приведенную выше статью о Mozilla в качестве руководства, я смог сохранить мою коллекцию в обратном порядке, используя следующий код, затем мы просто визуализируем коллекцию на странице в текущем порядке, и мы можем использовать флаг (reverseSortDirection) для обращая порядок.
//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;
}
},
Компаратор, за исключением двух моделей, при изменениях в коллекции или модели выполняет функцию compartor и изменяет порядок коллекции.
Я тестировал этот подход с Numbers и Strings, и, похоже, он отлично работает для меня.
Я действительно надеюсь, что этот ответ может помочь, поскольку я много раз боролся с этой проблемой и обычно заканчивал работу.
Ответ 8
Это можно сделать элегантно, переопределив метод sortBy. Вот пример
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');
}
});
Итак, вы можете использовать его следующим образом:
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();
Это решение не зависит от типа поля.
Ответ 9
// 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;
};
Ответ 10
Вот очень простое решение для тех, кто просто хочет перевернуть текущий порядок. Это полезно, если у вас есть таблица, столбцы которой можно упорядочить в двух направлениях.
collection.models = collection.models.reverse()
Вы можете комбинировать его с чем-то подобным, если вас интересует случай таблицы (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
Ответ 11
Для обратного порядка на id:
comparator: function(object) { return (this.length - object.id) + 1; }
Ответ 12
У меня было несколько другое требование, поскольку я показываю свои модели в таблице, и я хотел иметь возможность сортировать их по любой причине (свойство модели) и либо по восходящей, либо по убыванию.
Он потратил много усилий, чтобы заставить все дела работать (где значения могут быть строками, пустыми строками, датами, числами. Однако я думаю, что это довольно близко.
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;
}
},
использование:
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);
},
Ответ 13
Я просто переопределил функцию сортировки, чтобы повернуть массив моделей после завершения. Кроме того, я приостановил запуск события сортировки до тех пор, пока не завершится обратное.
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;
},
Ответ 14
Недавно столкнулся с этой проблемой и просто решил добавить метод 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;
}
});
Надеюсь, кто-то найдет это полезным
Ответ 15
Здесь можно легко и просто перерисовать модели коллекции с помощью UnderscoreJS
var reverseCollection = _.sortBy(this.models, function(model) {
return self.indexOf(model) * -1;
});