С Backbone.js у меня есть коллекция с функцией компаратора. Он хорошо сортирует модели, но я бы хотел изменить порядок.
Как я могу отсортировать модели по убыванию, а не по возрастанию?
javascript
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 использует метод _.sortBy в Underscore.js. Реализация 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 может иметь непредвиденные последствия.
источник
Мое решение заключалось в отмене результатов после сортировки.
Чтобы предотвратить двойной рендеринг, сначала выполните сортировку с отключением звука, а затем активируйте «сброс».
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; } },
Компаратор, кроме двух моделей, при изменении коллекции или модели запускается функция отсека и изменяется порядок коллекции.
Я протестировал этот подход с числами и строками, и, похоже, он отлично работает для меня.
Я действительно надеюсь, что этот ответ может помочь, поскольку я много раз боролся с этой проблемой и обычно в конечном итоге использую обходной путь.
источник
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();
Это решение не зависит от типа поля.
источник
// 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; };
источник
Вот действительно простое решение для тех, кто просто хочет перевернуть текущий порядок. Это полезно, если у вас есть таблица, столбцы которой можно упорядочить в двух направлениях.
Вы можете комбинировать это с чем-то вроде этого, если вас интересует настольный случай (coffeescript):
collection.comparator = (model) -> model.get(sortByType) collection.sort() if reverseOrder collection.models = collection.models.reverse()
источник
Для обратного порядка по id:
comparator: function(object) { return (this.length - object.id) + 1; }
источник
У меня было немного другое требование: я отображаю свои модели в таблице, и я хотел иметь возможность сортировать их по любому столбцу (свойству модели) и по возрастанию или по убыванию.
Потребовалось немало времени, чтобы заставить работать все случаи (где значения могут быть строками, пустыми строками, датами, числами. Однако я думаю, что это довольно близко.
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 до тех пор, пока не будет выполнено обратное.
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; });
источник