12

I found a great method to sort an array of objects based on one of the properties as defined at:

Sort array of objects by string property value in JavaScript

Using that function works perfectly for a single sort (on all browsers), and even a sort within another sort EXCEPT using Google Chrome! Here is Ege Özcan's great sort routine for arrays of objects

function dynamicSort(property) { 
    return function (a,b) {
        return (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
    }
}

Using an array named "Data" (of course, my array has many more object pairs)...

var Data = [{Category: "Business", Value: "ABC"},{Category:"Personal", Value:"XYZ"}];

I can get a proper sort where the order is listed as all the values within each category by doing this...

Data.sort(dynamicSort("Value"));
Data.sort(dynamicSort("Category"));

By first sorting on Value, and then by Category, my array puts all values in sorted order with all the Business-base values listed first and then all the Personal-based values. Perfect! Except in Chrome where the data is sorted properly by category, but the order of the values within each category seems rather random.

Does any one know of a better way to do a sort within a sort that would also work in Chrome?

Community
  • 1
  • 1
Colin Houghton
  • 131
  • 1
  • 1
  • 4
  • 2
    sorting by A and then independently sorting by B _is not the same as sorting by A **and** B_. If it worked on some browsers it was a fluke. – Alnitak Jul 08 '12 at 21:54

6 Answers6

41

I created a multi-parameter version of that dynamicSort function:

function dynamicSort(property) { 
    return function (obj1,obj2) {
        return obj1[property] > obj2[property] ? 1
            : obj1[property] < obj2[property] ? -1 : 0;
    }
}

function dynamicSortMultiple() {
    /*
     * save the arguments object as it will be overwritten
     * note that arguments object is an array-like object
     * consisting of the names of the properties to sort by
     */
    var props = arguments;
    return function (obj1, obj2) {
        var i = 0, result = 0, numberOfProperties = props.length;
        /* try getting a different result from 0 (equal)
         * as long as we have extra properties to compare
         */
        while(result === 0 && i < numberOfProperties) {
            result = dynamicSort(props[i])(obj1, obj2);
            i++;
        }
        return result;
    }
}

I created an array as follows:

var arr = [
    {a:"a",b:"a",c:"a"},
    {a:"b",b:"a",c:"b"},
    {a:"b",b:"a",c:"a"},
    {a:"b",b:"a",c:"b"},
    {a:"b",b:"b",c:"a"},
    {a:"b",b:"b",c:"b"},
    {a:"b",b:"b",c:"a"},
    {a:"b",b:"b",c:"b"},
    {a:"b",b:"b",c:"a"},
    {a:"b",b:"b",c:"b"},
    {a:"b",b:"b",c:"a"},
    {a:"c",b:"b",c:"b"},
    {a:"c",b:"c",c:"a"}
];

and it worked when I did,

arr.sort(dynamicSortMultiple("c","b","a"));

And here is a working example: http://jsfiddle.net/ZXedp/

Ege Özcan
  • 13,971
  • 2
  • 30
  • 51
  • 1
    this one does, also with multiple properties: http://stackoverflow.com/questions/1129216/sorting-objects-in-an-array-by-a-field-value-in-javascript/4760279#4760279 – Ege Özcan May 10 '13 at 08:44
  • Hello, this works amazing but how you do this by passing an array of properties like ["a", "b"] and other is when the property needs to be grater that for example [{a: {gt: 5}}] "gt is grater that" – Luis Monsalve Jun 01 '21 at 17:48
  • @LuisDanielMonsalveRuiz you can do dynamicSortMultiple(...myArray) for the "greater than" example, you want to filter the results, AFAICT. you may want to look at array.prototype.filter – Ege Özcan Dec 06 '21 at 10:19
8

The easiest way to perform a Javascript Multi-Criteria Sort (or Multi-Parameter Sort), is to use .sort, concatenate the multiple parameters together, and compare the two stings.

For example:

data.sort(function (a, b) {

  var aConcat = a["property1"] + a["property2"];
  var bConcat = b["property1"] + b["property2"];

  if (aConcat > bConcat) {
    return 1;
  } else if (aConcat < bConcat) {
    return -1;
  } else {
    return 0;
  }

});

I've included a JsFiddle Script here: http://jsfiddle.net/oahxg4u3/6/

tim-montague
  • 16,217
  • 5
  • 62
  • 51
3

I now this post is quite old, anyway I found it today and quoting Ege Özcan, I improved his excellent solution implementing DESC-ASC SQL-Like functionality for anyone interested (http://jsfiddle.net/ZXedp/65/):

function dynamicSortMultiple() {
    var props=[];
    /*Let's separate property name from ascendant or descendant keyword*/
    for(var i=0; i < arguments.length; i++){
        var splittedArg=arguments[i].split(/ +/);
        props[props.length]=[splittedArg[0], (splittedArg[1] ? splittedArg[1].toUpperCase() : "ASC")];
    }
    return function (obj1, obj2) {
        var i = 0, result = 0, numberOfProperties = props.length ;
        /*Cycle on values until find a difference!*/
        while(result === 0 && i < numberOfProperties) {
            result = dynamicSort(props[i][0], props[i][1])(obj1, obj2);
            i++;
        }
        return result;
    }
}

/*Base function returning -1,1,0 for custom sorting*/
function dynamicSort(property, isAscDesc) { 
    return function (obj1,obj2) {
        if(isAscDesc==="DESC"){
            return ((obj1[property] > obj2[property]) ? (-1) : ((obj1[property] < obj2[property]) ? (1) : (0)));
        }
        /*else, if isAscDesc==="ASC"*/
        return ((obj1[property] > obj2[property]) ? (1) : ((obj1[property] < obj2[property]) ? (-1) : (0)));
    }
}

call the function by something like this:

arr.sort(dynamicSortMultiple("c DESC","b Asc","a"));
Enrico D.
  • 31
  • 1
2

You may also want to have a look at thenBy.js: https://github.com/Teun/thenBy.js

It allows you to use the standard Array.sort, but with firstBy().thenBy().thenBy() style.

Teun D
  • 5,045
  • 1
  • 34
  • 44
  • Better: `firstBy = (function() { function tb(y) { var x=this; function f(a,b) { return x(a,b)||y(a,b); } f.thenBy = tb; return f; } return function(f) { f.thenBy = tb; return f; }; })();` – Bergi Aug 04 '13 at 17:46
  • I'm interested in why this is better. Could you detail? Maybe using a pull request? – Teun D Aug 04 '13 at 20:47
  • 1) only one, shared `thenBy` function 2) no unnecessary `secondaryFunction` 3) it's shorter 4) it's more functional, yours would've problems with `var a = firstBy(x), b = a.thenBy(y), c = a.thenBy(z)` because `a`==`b`==`c` – Bergi Aug 05 '13 at 09:09
  • Thanks. I didn't see much of a reason to do the whole function-call-wrap circus here, but maybe you're right. I will play a bit with both and credit you if I replace it with your version. – Teun D Aug 05 '13 at 11:42
  • Got my version down to 137 characters. See https://github.com/bergus/thenBy.js :-) – Bergi Aug 05 '13 at 12:13
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/34845/discussion-between-teun-d-and-bergi) – Teun D Aug 05 '13 at 14:46
  • Thank you for this reference, I messed with multi-key sorting for longer than I care to admit, then within 5 minutes it was solved by thenBy. Thanks again! – Zacho Dec 07 '16 at 17:13
0

Here is my solution. It faster than lodash's _.sortBy() multi-column sort function in about two times (see http://jsperf.com/multi-column-sort. I generate text of sorting function, then use it in standard .sort(). It works in Chrome and Firefox as well.

function multiColumnSort(arr,sf) {
    var s = '';
    sf.forEach(function(f,idx) {
        s += 'if(arguments[0].'+f+'>arguments[1].'+f+')return 1;';
        s += 'else if(arguments[0].'+f+'==arguments[1].'+f+')';
        s += (idx < sf.length-1)? '{' : 'return 0';
    });
    s += Array(sf.length).join('}')+';return -1';
    return arr.sort(new Function(s));
};
agershun
  • 4,077
  • 38
  • 41
0

let obj = [{ name: "Gaurav" }, { name: "nidhu" }, { name: "Abhishek" }, { name: "cat" }, { name: "here" }];
for (let i = 0; i < obj.length; i++) {
  for (let j = 1; j < obj.length; j++) {
    if (obj[j - 1].name[0].toLowerCase() > obj[j].name[0].toLowerCase()) {
      let temp = obj[j - 1];
      obj[j - 1] = obj[j]
      obj[j] = temp
    }
  }
}
console.log(obj)
Gaurav
  • 11
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 13 '22 at 16:37