Mike Mike - 7 months ago 28
Javascript Question

How to sort an array of objects by multiple fields?

From this original question, how would I apply a sort on multiple fields?

Using this slightly adapted structure, how would I sort city (ascending) & then price (descending)?

var homes = [
{"h_id":"3",
"city":"Dallas",
"state":"TX",
"zip":"75201",
"price":"162500"},
{"h_id":"4",
"city":"Bevery Hills",
"state":"CA",
"zip":"90210",
"price":"319250"},
{"h_id":"6",
"city":"Dallas",
"state":"TX",
"zip":"75000",
"price":"556699"},
{"h_id":"5",
"city":"New York",
"state":"NY",
"zip":"00010",
"price":"962500"}
];


I liked the fact than an answer was given which provided a general approach. Where I plan to use this code, I will have to sort dates as well as other things. The ability to "prime" the object seemed handy, if not a little cumbersome.

I've tried to build this answer into a nice generic example, but I'm not having much luck.

Answer

A multi dimensional sorting method, based on this answer:

Update: Here is an "optimized" version. It does a lot more preprocessing and creates a comparison function for each sorting option beforehand. It might need more more memory (as it stores a function for each sorting option, but it should preform a bit better as it does not have to determine the correct settings during the comparison. I have not done any profiling though.

var sort_by;

(function() {
    // utility functions
    var default_cmp = function(a, b) {
            if (a == b) return 0;
            return a < b ? -1 : 1;
        },
        getCmpFunc = function(primer, reverse) {
            var dfc = default_cmp, // closer in scope
                cmp = default_cmp;
            if (primer) {
                cmp = function(a, b) {
                    return dfc(primer(a), primer(b));
                };
            }
            if (reverse) {
                return function(a, b) {
                    return -1 * cmp(a, b);
                };
            }
            return cmp;
        };

    // actual implementation
    sort_by = function() {
        var fields = [],
            n_fields = arguments.length,
            field, name, reverse, cmp;

        // preprocess sorting options
        for (var i = 0; i < n_fields; i++) {
            field = arguments[i];
            if (typeof field === 'string') {
                name = field;
                cmp = default_cmp;
            }
            else {
                name = field.name;
                cmp = getCmpFunc(field.primer, field.reverse);
            }
            fields.push({
                name: name,
                cmp: cmp
            });
        }

        // final comparison function
        return function(A, B) {
            var a, b, name, result;
            for (var i = 0; i < n_fields; i++) {
                result = 0;
                field = fields[i];
                name = field.name;

                result = field.cmp(A[name], B[name]);
                if (result !== 0) break;
            }
            return result;
        }
    }
}());

Example usage:

homes.sort(sort_by('city', {name:'price', primer: parseInt, reverse: true}));

DEMO


Original function:

var sort_by = function() {
   var fields = [].slice.call(arguments),
       n_fields = fields.length;

   return function(A,B) {
       var a, b, field, key, primer, reverse, result, i;

       for(i = 0; i < n_fields; i++) {
           result = 0;
           field = fields[i];

           key = typeof field === 'string' ? field : field.name;

           a = A[key];
           b = B[key];

           if (typeof field.primer  !== 'undefined'){
               a = field.primer(a);
               b = field.primer(b);
           }

           reverse = (field.reverse) ? -1 : 1;

           if (a<b) result = reverse * -1;
           if (a>b) result = reverse * 1;
           if(result !== 0) break;
       }
       return result;
   }
};

DEMO

Comments