З Backbone.js у мене створена колекція з функцією порівняння. Це чудово сортує моделі, але я хотів би змінити порядок.
Як я можу сортувати моделі за спаданням, а не за зростанням?
З Backbone.js у мене створена колекція з функцією порівняння. Це чудово сортує моделі, але я хотів би змінити порядок.
Як я можу сортувати моделі за спаданням, а не за зростанням?
Відповіді:
Ну, ви можете повернути негативні значення з компаратора. Якщо ми візьмемо, наприклад, приклад із сайту 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'));
Особисто я не дуже задоволений будь-яким із наведених тут рішень:
Рішення множення на -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поводиться по-різному, якщо функція сортування має два аргументи. У цьому випадку він поводиться так само, як і переданий компаратор Array.sort. Це рішення працює шляхом адаптації функції одного аргументу sortBy до порівняльника двох аргументів та реверсування результату.
Порівняльник колекцій 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, ви можете зробити код чіткішим, витягнувши рядок negator і додавши його як метод у String.Prototype. Однак ви, мабуть, хочете зробити це, лише якщо знаєте, що робите, оскільки зміна власних типів об’єктів у javascript може мати непередбачені наслідки.
Моє рішення було змінити результати після сортування.
Щоб запобігти подвійному візуалізації, спочатку сортуйте за допомогою безшумного, а потім запускайте "скидання".
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
-Date.getTime(), можливо, з додатковим злому, якщо ви вважаєте, що дати у вашій системі перейдуть епоху unix. Для алфавіту зверніться до відповіді Ендрю вище.
Змініть функцію порівняння, щоб повернути якесь пропорційно пропорційне значення, замість того, щоб повертати дані, якими ви зараз є. Деякий код з: 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"}));
Для мене добре працювали:
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
Я прочитав десь, де функція порівняння 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 and Strings, і, здається, він ідеально працює для мене.
Я дуже сподіваюся, що ця відповідь може допомогти, оскільки я вже багато разів боровся з цією проблемою, і зазвичай в кінцевому підсумку використовую роботу.
sortKeyі reverseSortDirectionі виклик .sort()з будь-якої точки зору , яка має ручку колекції. Мені простіше, ніж інші голосові відповіді.
Це можна зробити елегантно, перевизначивши метод 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();
Це рішення не залежить від типу поля.
Ось справді просте рішення для тих, хто просто хоче змінити поточне замовлення. Це корисно, якщо у вас є таблиця, стовпці якої можна впорядкувати в двох напрямках.
collection.models = collection.models.reverse()
Ви можете поєднати це з чимось подібним, якщо вас цікавить корпус таблиці (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
У мене була дещо інша вимога, оскільки я відображаю свої моделі в таблиці, і я хотів мати можливість сортувати їх за будь-яким стовпцем (властивість моделі) і за зростанням, і за спаданням.
Для того, щоб спрацювати всі справи (де значення можуть бути рядками, порожніми рядками, датами, числами, знадобилося чимало муру), проте я думаю, що це досить близько.
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);
},
Я просто замінив функцію сортування, щоб повернути масив моделей після завершення. Також я утримував спрацьовування події "сортування", поки не буде завершено зворотне.
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;
},
Нещодавно натрапив на цю проблему і просто вирішив додати метод 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;
}
});
Сподіваємось, комусь це стане в нагоді
Ось швидкий і простий спосіб відсортувати моделі колекції за допомогою UnderscoreJS
var reverseCollection = _.sortBy(this.models, function(model) {
return self.indexOf(model) * -1;
});