Сортировка строк в обратном порядке с помощью backbone.js
Я пытаюсь сортировать коллекцию 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?
Ответы
Ответ 1
Существуют две версии функции сравнения, которые вы можете использовать, либо sortBy, которая была показана в примере, которая принимает один параметр, или sort - который вы можете вернуть более стандартную функцию сортировки, которую документация говорит:
"sortBy" функции-компараторы берут модель и возвращают числовое или строковое значение, с помощью которого модель должна быть упорядочена относительно других. Функции сортировки "сортировать" принимают две модели и возвращают -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"
Ответ 2
Вы можете:
- возьмите код char для каждого символа в строке,
- вычесть каждое значение из
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 кбайтах или более), вы можете столкнуться с проблемами (см. комментарий Matt ниже). Чтобы избежать этого и немного ускорить сравнение, просто используйте более короткий фрагмент вашей строки сравнения. (В приведенном выше примере вы могли бы пойти вместо chapter.get("title").slice(0, 100).split("")
.) Сколько времени потребуется вам в срезе, будет зависеть от вашего приложения.
Ответ 3
Если вы работаете с не численными значениями, нет очевидного способа сделать обратную сортировку. Магистраль использует методы _.sortBy()
и _.sortedIndex()
из Underscore для заказа моделей на основе компаратора, и эти методы автоматически сортируются в порядке возрастания. Наивный способ сделать это - использовать chapters.pluck('title').reverse()
, поскольку результатом pluck
будет массив. Но вызов reverse
в некоторых методах коллекции изменит модели коллекции на месте, поэтому в следующий раз, когда вы его вызовете, модели вернутся в порядке возрастания. Вы всегда можете сделать что-то вроде:
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-ninjary в методе компаратора - обратите внимание, что это не проверено:
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" и т.д., что должно создайте обратный сортир, который вам нужен.
Ответ 4
Поскольку Backbone просто использует метод .sortBy, просто прокси-сервер в вашей собственной логике:
collectionInQuestion.sortBy = function () {
var models = _.sortBy(this.models, this.comparator);
if (forSomeReason) {
models.reverse();
}
return models;
};
.. или добавить его в другое место.
TweakedCollection = Backbone.Collection.extend({ sortBy: [...] })
Ответ 5
Я только что решил аналогичную проблему с сортировкой таблицы, и мне захотелось разделить код, так как я не нашел много помощи в этих ответах:
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();
}
},
в этом случае я просто устанавливаю компаратор в строку вместо функции; строка должна быть именем свойства, которое вы хотите отсортировать.
Затем я просто вызываю реверс на моделях, если порядок должен быть обратным.
Ответ 6
Просто добавьте минус до chapter.get
chapters.comparator = function(chapter) {
return -chapter.get("title");
};