Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a multi-level sort in comparator in Backbone.js/underscore.js?

Imagine a Model/Collection like:

var AModel = Backbone.Model.extend({
    defaults: {
        a: 'a string',
        b: 'another string',
        c: 'yet another string'
    }
});

var ACollection = Backbone.Collection.extend({
    model: AModel,
    comparator: function(amodel) {
        ...
    }
});

How could I write a comparator to implement a multi-level sort? I'd like to sort by AModel's a attribute, then by its b attribute, and then by its c attribute.

I've hacked together a comparator that looks something like this, but I want to know if there is a better/smarter way?

comparator: function(amodel) {
    var s = '',
        assumed_max_length_of_any_attribute = 30;

    s += amodel.get('a');
    while (s.length < assumed_max_length_of_any_attribute) {
        s += ' ';
    }

    s += amodel.get('b');
    while (s.length < assumed_max_length_of_any_attribute) {
        s += ' ';
    }

    s += amodel.get('c');
    while (s.length < assumed_max_length_of_any_attribute) {
        s += ' ';
    }

    return s;
}

Then, s becomes properly padded with spaces and should be in "lexical" order with multiple levels. But it all feels very gross compared to the beauty of python's stable multi-level sort (if somehow the above had analogous equivalents in python):

collection.sort(key=lambda x: x.get('c'))
collection.sort(key=lambda x: x.get('b'))
collection.sort(key=lambda x: x.get('a'))

Is there a better way?

like image 964
dlamotte Avatar asked Feb 08 '12 20:02

dlamotte


1 Answers

The backbone documentation says:

Comparator function can be defined as either a sortBy (pass a function that takes a single argument), or as a sort (pass a comparator function that expects two arguments).

http://documentcloud.github.com/backbone/#Collection-comparator

You could use the second way and implement your comparison based on two elements given.

Maybe something like this:

helper: function (c1, c2) {
    if (c1 < c2) return -1;
    if (c1 > c2) return +1;
    return 0;
}
comparator: function (model1, model2) {
    return _.reduce(["c", "b", "a"], function (acc, comp) {
        return acc !== 0 ? acc : this.helper(model1[comp], model2[comp])
    }, 0);
}
like image 60
knub Avatar answered Oct 13 '22 02:10

knub