With Backbone.js I\'ve got a collection set up with a comparator function. It\'s nicely sorting the models, but I\'d like to reverse the order.
How can I sort the m
Modify your comparator function to return some reversely proporitional value instead of returning the data that you are currently. Some code from : http://documentcloud.github.com/backbone/#Collection-comparator
Example:
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"}));
This can be done elegantly by overriding sortBy method. Here is an example
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');
}
});
So you can use it like this:
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();
This solution does not depend on the field type.
Personally, I'm not that happy with any of the solutions given here:
The multiplying by -1 solution fails to work when the sort type is non-numeric. Although there are ways around this (by calling Date.getTime()
for example) these cases are specific. I would like a general way to reverse the direction of any sort, without having to worry about the specific type of the field being sorted.
For the string solution, constructing a string one character at a time seems like a performance bottleneck, especially when using large collections (or indeed, large sort field strings)
Here's a solution that works nicely for String, Date and Numeric fields:
Firstly, declare a comparator that will reverse the result of a result of a sortBy function, like this:
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;
};
}
Now, if you want to reverse the direction of the sort, all we need to do is:
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'));
The reason this works is that Backbone.Collection.sort
behaves differently if the sort function has two arguments. In this case, it behaves in the same manner as the comparator passed into Array.sort
. This solution works by adapting the single argument sortBy function into a two argument comparator and reversing the result.
Well, you can return negative values from comparator. If we take, for example, the example from Backbone's site and want to reverse the order, it will look like this:
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'));
// 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;
};
Here is a really simple solution, for those who simply want to flip the current order. It is useful if you have a table whose columns can be ordered in two directions.
collection.models = collection.models.reverse()
You can combine it with something like this if you are interested in the table case (coffeescript) :
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()