Question

I am trying to sort a collection of models by a creation date or priority, selectable by a user.

Priority can be selected in either descending or ascending order but the models are then further sub-ordered by creation date in descending order.

Creation date can also be selected in either descending or ascending order with no further sub ordering.

Below is the code I have so far:

comparator: function(task) {
  var sorter = task.get(root.sortAttr);
  var subSorter = String.fromCharCode.apply(String,
    _.map(task.get("created_at").split(""), function (c) {
      return 0xffff - c.charCodeAt();
    })
  );

  if(!root.sortAscending) {
    console.log("desc");
    return -sorter + ", " + subSorter;
  }
  else {
    console.log("asc");
    return sorter + ", " + subSorter;
  }
}

This code works for both ascending and descending ordering of creation date, but will only ever show priorities in descending order. Can anyone tell me why this is the case and what I can do to fix that?

Was it helpful?

Solution 2

I ended up using a purely string based approach which has yielded the result I was after and offers more flexibility in terms of being able to support string based sort attributes down the track.

this.tasks.comparator = function(task) {
  var priority = (task.get("priority_id")).toString();
  var timestamp = task.get("created_at");

  if (!sortAscending) {
    if (sortAttribute == "created_at") return negateString(timestamp);
    if (sortAttribute == "priority_id") return negateString(negateString(priority) + "-" + negateString(timestamp));
  }
  else {
    if (sortAttribute == "created_at") return timestamp;
    if (sortAttribute == "priority_id") return negateString(priority) + "-" + timestamp;
  }
}

this.tasks.sort();

Where the negate string function is the same one found here and reproduced below as follows:

var negateString = function(s) {
  s = s.toLowerCase();
  s = s.split("");
  s = _.map(s, function(letter) { 
    return String.fromCharCode(-(letter.charCodeAt(0)));
  });

  return s.join("");
};

OTHER TIPS

Ok, lets presume that you have an attribute priority in your model.

Easy solution for it is:

sortOrder: 'asc',
comparator: function (item) {
  var timestamp = Date.parse(item.get('created_at')) * 1000;
  timestamp += item.get('priority');

  if (this.sortOrder === 'asc') {
    return timestamp;
  }

  return -timestamp;
}

More sophisticated solution:

sortBy: {'created_at': 'asc', 'priority': 'desc'},
comparator: function (item) {
  var result = 0;
  for (var field in this.sortBy) {
      if (field === 'created_at') {
          result += (this.sortBy[field] === 'asc')
                 ? Date.parse(item.get('created_at')) * 1000
                 : -Date.parse(item.get('created_at')) * 1000;
      }

      if (field === 'priority') {
          result += (this.sortBy[field] === 'asc')
                 ? item.get(field)) * 1000
                 : -item.get(field)) * 1000;
      }
  }

  return result;
}

You can extend sortBy to work with other fields, just parse it to int. Attention your self, will fail when the number becomes > Number.MAX_VALUE or < Number.MIN_VALUE.

You can implement a toInt() method for your fields, and use all of it without the if inside the object loop.

result += (this.sortBy[field] === 'asc')
        ? item.get(field).toInt() * 1000
        : -item.get(field).toInt() * 1000;

backbone now takes two models that you can compare, simply add the second parameter to the comparator function. Not sure why the docs have not been updated.

comparator: function (model, model2) {
  return (model1.get('some_attribute') > model2.get('some_attribute')) ? -1 : 1;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top