Сортировка строк в обратном порядке с помощью backbone.js - PullRequest
40 голосов
/ 12 апреля 2011

Я пытаюсь отсортировать коллекцию Backbone.js в обратном порядке. Есть предыдущие ответы о том, как это сделать с целыми числами, но нет ответов со строками.

var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

chapters.comparator = function(chapter) {
  return chapter.get("title");
};

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'));

Приведенный выше код сортирует главы из A -> Z , но как мне написать компаратор, который сортирует его из Z -> A ?

Ответы [ 6 ]

45 голосов
/ 12 апреля 2011

Вы можете:

  • получить код символа для каждого символа в строке,
  • вычесть каждое значение из 0xffff (максимальное возвращаемое значение string.charCodeAt),
  • используйте String.fromCharCode, чтобы превратить это в строку "отрицательных" символов

, и это будет ваш ключ сортировки.

chapters.comparator = function(chapter) {
    return String.fromCharCode.apply(String,
        _.map(chapter.get("title").split(""), function (c) {
            return 0xffff - c.charCodeAt();
        })
    );
}

И вуаля:

> console.log(chapters.pluck('title'));
["The Middle", "The End", "The Beginning"]

Примечание: если строки сравнения длинные (например, 65 КБ или более), вы можете столкнуться с проблемами (см. Комментарий Мэтта ниже).Чтобы избежать этого и немного ускорить сравнение, просто используйте более короткий фрагмент вашей строки сравнения.(В приведенном выше примере вы могли бы вместо этого указать chapter.get("title").slice(0, 100).split("").) Как долго необходимый вам срез будет зависеть от вашего приложения.

20 голосов
/ 23 августа 2012

Существует две версии функции компаратора, которую вы можете использовать: либо sortBy версия - которая была показана в примере, которая принимает один параметр, либо sort - которую вы может вернуть более стандартную функцию сортировки, которая в документации гласит:

Функции компаратора sortBy берут модель и возвращают числовое или строковое значение, по которому модель должна быть упорядочена относительно других. Функции сравнения «sort» берут две модели и возвращают -1, если первая модель должна предшествовать второй, 0, если они имеют одинаковый ранг, и 1, если первая модель следует после.

Итак, в этом случае мы можем написать другую функцию компаратора:

var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

chapters.comparator = function(chapterA, chapterB) {
  if (chapterA.get('title') > chapterB.get('title')) return -1; // before
  if (chapterB.get('title') > chapterA.get('title')) return 1; // after
  return 0; // equal
};

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'));

Итак, вы должны получить в ответ:

"The Middle", "The End", "The Beginning"
12 голосов
/ 31 декабря 2011

Поскольку Backbone просто использует метод .sortBy, просто прокси в вашей собственной логике:

collectionInQuestion.sortBy = function () {
  var models = _.sortBy(this.models, this.comparator);
  if (forSomeReason) {
    models.reverse();
  }
  return models;
};

.. или добавить его где-нибудь еще ..

TweakedCollection = Backbone.Collection.extend({ sortBy: [...] })
12 голосов
/ 12 апреля 2011

Если вы работаете с нечисловыми значениями, не существует очевидного способа сделать обратную сортировку. Backbone использует методы _.sortBy() и _.sortedIndex() от Underscore для упорядочивания моделей на основе компаратора, и эти методы автоматически сортируются в порядке возрастания. Наивный способ сделать это - использовать chapters.pluck('title').reverse(), в результате pluck будет массивом. Но вызов reverse для некоторых методов Collection приведет к изменению моделей Collection на месте, поэтому при следующем вызове модели вернутся в порядке возрастания. Вы всегда можете сделать что-то вроде:

var results = [],
    titles  = chapters.pluck('title');

for(var i=0, len=titles.length; i<len; i++) {
  results.push(titles[i]);
}

results.reverse();

Это не повлияет на массив моделей в вашей коллекции Backbone, так как это создаст совершенно новый массив результатов в памяти, но сохранит ссылки на исходные модели, поэтому при вызове таких вещей, как save, все равно будет обновлено состояние Collection.

Но это не очень элегантно и создает много дополнительного кодирования в вашем проекте в любое время, когда вы захотите обратить вспять результаты. Я думаю, что мы можем сделать лучше.

Чтобы сделать эту работу, вам нужно выполнить немного громоздкий ниджалир JavaScript в вашем методе сравнения, чтобы сделать эту работу - обратите внимание, что это не проверено:

chapters.comparator = function(chapter) {
  var alphabet = '0123456789abcdefghijklmnopqrstuvwxyz',
      title = chapter.get('title').toLowerCase(),
      inverse_title = '',
      index;

  for(var i=0, len=title.length; i<len; i++) {
    index = alphabet.indexOf(title.charAt(i));

    if(index === -1) {
      inverse_title += title.charAt(i);
      continue;
    }

    inverse_title += alphabet.charAt(alphabet.length - index - 1);
  }

  return inverse_title;
};

Эта концепция, вероятно, нуждается в улучшении, чтобы учесть символы и т. Д., Но по существу она инвертирует строку компаратора таким образом, что "Z" становится "0", "Y" становится "1" и т. Д., Что должно произвести обратную сортировку, которую вы ищете.

0 голосов
/ 25 апреля 2013

Я только что решил похожую проблему с сортировкой таблиц, и я хотел поделиться кодом, так как я не нашел большой помощи в этих ответах:

events: {

    'click th.sortable': function(e) {
        var $this = $(e.target),
            order = $this.hasClass('asc') ? 'desc' : 'asc',
            field = $this.data('field'); /* this is a string */

        $this.siblings().addBack().removeClass('asc desc');
        $this.addClass( order );

        this.bodyView.collection.comparator = field;
        this.bodyView.collection.sort();
        if ( order === 'desc' ) this.bodyView.collection.models.reverse();

        this.bodyView.render();
    }

},

в этом случае я просто устанавливаю компаратор в строку вместо функции; строка должна быть именем свойства, по которому вы хотите отсортировать. Тогда я просто вызываю реверс на моделях, если порядок должен быть обратным.

0 голосов
/ 28 июля 2012

Просто добавьте минус перед chapter.get

chapters.comparator = function(chapter) {
    return -chapter.get("title");
};      
...