Обратный порядок сортировки с помощью 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;
 });