343

Let's say you have a JavaScript class like this

var DepartmentFactory = function(data) {
    this.id = data.Id;
    this.name = data.DepartmentName;
    this.active = data.Active;
}

Let's say you then create a number of instances of that class and store them in an array

var objArray = [];
objArray.push(DepartmentFactory({Id: 1, DepartmentName: 'Marketing', Active: true}));
objArray.push(DepartmentFactory({Id: 2, DepartmentName: 'Sales', Active: true}));
objArray.push(DepartmentFactory({Id: 3, DepartmentName: 'Development', Active: true}));
objArray.push(DepartmentFactory({Id: 4, DepartmentName: 'Accounting', Active: true}));

So I now would have an array of objects created by DepartmentFactory. How would I go about using the array.sort() method to sort this array of objects by the DepartmentName property of each object?

The array.sort() method works just fine when sorting an array of strings

var myarray=["Bob", "Bully", "Amy"];
myarray.sort(); //Array now becomes ["Amy", "Bob", "Bully"]

But how do I make it work with a list of objects?

Luca Kiebel
  • 9,790
  • 7
  • 29
  • 44
jdavis
  • 8,255
  • 14
  • 54
  • 62
  • You can pass a sort function as the first argument to .sort(). – Paul Tomblin Jan 17 '12 at 19:47
  • 2
    Since you're using `DepartmentFactory` as a constructor, create its objects using `new DepartmentFactory`, otherwise the array will be filled with a bunch of `undefined` values. – Anurag Jan 17 '12 at 19:49

14 Answers14

513

you would have to do something like this:

objArray.sort(function(a, b) {
    var textA = a.DepartmentName.toUpperCase();
    var textB = b.DepartmentName.toUpperCase();
    return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
});

note: changing the case (to upper or lower) ensures a case insensitive sort.

Omer Bokhari
  • 57,458
  • 12
  • 44
  • 58
  • 7
    I've come to your answer 50 gazillion times because i can never remember how to do this. FWIW, my Linter always informs me of "gratuitous parentheses around expression": `return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;` Not gonna edit your answer, holding out hope I'll at least remember the linting thing :) – MikeT May 18 '18 at 22:40
  • Found an issue with this code. Use a combination of lower and capital letters. ex. credit_card_no and City. The code does sort the list but words starting with 'c' are not grouped together. – Kedar Aug 21 '19 at 21:55
  • 6
    we can simplify it to return textA.localeCompare(textB); – Taran Feb 04 '21 at 21:47
260

To support unicode:

objArray.sort(function(a, b) {
   return a.DepartmentName.localeCompare(b.DepartmentName);
});
ron tornambe
  • 10,452
  • 7
  • 33
  • 60
  • 15
    For a case-insensitive version, use the following on line 2: `return a.DepartmentName.toLowerCase().localeCompare(b.DepartmentName.toLowerCase());` – Ben Barreth Jan 16 '18 at 16:09
  • 5
    learn something new every day - `localeCompare` is cool and has all browser support for first arg. Not bad! – obzenner Jun 15 '18 at 21:34
  • 13
    @BenBarreth there is no reason to lower-case the strings. the whole point of `localeCompare` is to shunt the work of managing sort logic and locale quirks to the system. If doing a case-insensitive sort is normal for the locale, as it is in english, this will be done for you: `"Z" > 'a' // false` `"Z".localeCompare('a') // 1` If you wish to deviate from the locale's default, you can send along overrides in the `locales` and `options` parameters: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#Parameters – jrz Mar 08 '19 at 14:24
  • Just remember that this isn't null safe, need to check to see if "DepartmentName" is an actual string before running this otherwise the sort will fail on a null pointer exception. – Kris Boyd Jun 14 '21 at 20:42
47

Shorter code with ES6

objArray.sort((a, b) => a.DepartmentName.toLowerCase().localeCompare(b.DepartmentName.toLowerCase()))
Ludovic
  • 501
  • 5
  • 10
20
objArray.sort((a, b) => a.DepartmentName.localeCompare(b.DepartmentName))
Neil
  • 7,861
  • 4
  • 53
  • 74
14
var DepartmentFactory = function(data) {
    this.id = data.Id;
    this.name = data.DepartmentName;
    this.active = data.Active;
}

// use `new DepartmentFactory` as given below. `new` is imporatant

var objArray = [];
objArray.push(new DepartmentFactory({Id: 1, DepartmentName: 'Marketing', Active: true}));
objArray.push(new DepartmentFactory({Id: 2, DepartmentName: 'Sales', Active: true}));
objArray.push(new DepartmentFactory({Id: 3, DepartmentName: 'Development', Active: true}));
objArray.push(new DepartmentFactory({Id: 4, DepartmentName: 'Accounting', Active: true}));

function sortOn(property){
    return function(a, b){
        if(a[property] < b[property]){
            return -1;
        }else if(a[property] > b[property]){
            return 1;
        }else{
            return 0;   
        }
    }
}

//objArray.sort(sortOn("id")); // because `this.id = data.Id;`
objArray.sort(sortOn("name")); // because `this.name = data.DepartmentName;`
console.log(objArray);

demo: http://jsfiddle.net/diode/hdgeH/

Diode
  • 24,570
  • 8
  • 40
  • 51
9
objArray.sort( (a, b) => a.id.localeCompare(b.id, 'en', {'sensitivity': 'base'}));

This sorts them alphabetically AND is case insensitive. It's also super clean and easy to read :D

Alissa
  • 91
  • 1
  • 1
  • This is also the only answer which mentions `'sensitivity': 'base'`, which makes a big difference depending on your application [according to MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator). – Jules Jul 03 '20 at 05:57
  • thanks https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#sort_an_array – bilelz Feb 09 '23 at 13:09
8
// Sorts an array of objects "in place". (Meaning that the original array will be modified and nothing gets returned.)
function sortOn (arr, prop) {
    arr.sort (
        function (a, b) {
            if (a[prop] < b[prop]){
                return -1;
            } else if (a[prop] > b[prop]){
                return 1;
            } else {
                return 0;   
            }
        }
    );
}

//Usage example:

var cars = [
        {make:"AMC",        model:"Pacer",  year:1978},
        {make:"Koenigsegg", model:"CCGT",   year:2011},
        {make:"Pagani",     model:"Zonda",  year:2006},
        ];

// ------- make -------
sortOn(cars, "make");
console.log(cars);

/* OUTPUT:
AMC         : Pacer : 1978
Koenigsegg  : CCGT  : 2011
Pagani      : Zonda : 2006
*/



// ------- model -------
sortOn(cars, "model");
console.log(cars);

/* OUTPUT:
Koenigsegg  : CCGT  : 2011
AMC         : Pacer : 1978
Pagani      : Zonda : 2006
*/



// ------- year -------
sortOn(cars, "year");
console.log(cars);

/* OUTPUT:
AMC         : Pacer : 1978
Pagani      : Zonda : 2006
Koenigsegg  : CCGT  : 2011
*/
bob
  • 7,539
  • 2
  • 46
  • 42
5

Because all of the solutions here were presented without null/undefined safe operations, I handle that this way (you can handle nulls as you see fit):

ES5

objArray.sort(
  function(a, b) {
    var departmentNameA = a.DepartmentName ? a.DepartmentName : '';
    var departmentNameB = b.DepartmentName ? b.DepartmentName : '';

    return departmentNameA.localeCompare(departmentNameB);
  }
);

ES6+

objArray.sort(
 (a: DepartmentFactory, b: DepartmentFactory): number => {
   const departmentNameA = a.DepartmentName ? a.DepartmentName : '';
   const departmentNameB = b.DepartmentName ? b.DepartmentName : '';

   return departmentNameA.localeCompare(departmentNameB);
 }
);

I have also removed the toLowerCase that others used since localeCompare is case insensitive. Also I prefer to be a bit more explicit on parameters when using Typescript or ES6+ to make it more explicit for future developers.

Kris Boyd
  • 798
  • 10
  • 17
  • 1
    Neither sort actually returns anything so no sorting is happening. Also you can shorten the ternary to use a null coalescing operator (??). – RogerSmith Apr 02 '23 at 14:56
  • @RogerSmith thanks for the comment! You are correct, I mistakenly forgot to return the localeCompare result. The ?? operator was introduced in ES2020, so in 2021 I would have hesitated using anything that fresh due to browser compatibility issues with raw JS. Also quick note, ?? is the "nullish coalescing operator" the null coalescing operator is xxx?.yyy... Its a minor semantic difference in names, but big implication in implementation. Nullish works just as weirdly as Falsey and has a slightly different precedence than ternary. I lean towards ternary for compatibility and explicit syntax. – Kris Boyd Apr 06 '23 at 17:50
5

DEMO

var DepartmentFactory = function(data) {
    this.id = data.Id;
    this.name = data.DepartmentName;
    this.active = data.Active;
}

var objArray = [];
objArray.push(new DepartmentFactory({Id: 1, DepartmentName: 'Marketing', Active: true}));
objArray.push(new DepartmentFactory({Id: 2, DepartmentName: 'Sales', Active: true}));
objArray.push(new DepartmentFactory({Id: 3, DepartmentName: 'Development', Active: true}));
objArray.push(new DepartmentFactory({Id: 4, DepartmentName: 'Accounting', Active: true}));

console.log(objArray.sort(function(a, b) { return a.name > b.name}));
qwertymk
  • 34,200
  • 28
  • 121
  • 184
2

do it like this

objArrayy.sort(function(a, b){
 var nameA=a.name.toLowerCase(), nameB=b.name.toLowerCase()
 if (nameA < nameB) //sort string ascending
  return -1
 if (nameA > nameB)
  return 1
 return 0 //default return value (no sorting)
});
console.log(objArray)
nickleefly
  • 3,733
  • 1
  • 29
  • 31
1

Here is a simple function you can use to sort array of objects through their properties; it doesn't matter if the property is a type of string or integer, it will work.

    var cars = [
        {make:"AMC",        model:"Pacer",  year:1978},
        {make:"Koenigsegg", model:"CCGT",   year:2011},
        {make:"Pagani",     model:"Zonda",  year:2006},
    ];

    function sortObjectsByProp(objectsArr, prop, ascending = true) {
        let objectsHaveProp = objectsArr.every(object => object.hasOwnProperty(prop));
        if(objectsHaveProp)    {
            let newObjectsArr = objectsArr.slice();
            newObjectsArr.sort((a, b) => {
                if(isNaN(Number(a[prop])))  {
                    let textA = a[prop].toUpperCase(),
                        textB = b[prop].toUpperCase();
                    if(ascending)   {
                        return textA < textB ? -1 : textA > textB ? 1 : 0;
                    } else {
                        return textB < textA ? -1 : textB > textA ? 1 : 0;
                    }
                } else {
                    return ascending ? a[prop] - b[prop] : b[prop] - a[prop];
                }
            });
            return newObjectsArr;
        }
        return objectsArr;
    }

    let sortedByMake = sortObjectsByProp(cars, "make"); // returns ascending order by its make;
    let sortedByYear = sortObjectsByProp(cars, "year", false); // returns descending order by its year,since we put false as a third argument;
    console.log(sortedByMake);
    console.log(sortedByYear);
-3

After try a little bit on this, and trying to make less loops as possible, I ended up with this solution:

Demo on codepen

const items = [
      {
        name: 'One'
      },
      {
        name: 'Maria is here'
      },
      {
        name: 'Another'
      },
      {
        name: 'Z with a z'
      },
      {
        name: '1 number'
      },
      {
        name: 'Two not a number'
      },
      {
        name: 'Third'
      },
      {
        name: 'Giant'
      }
    ];

    const sorted = items.sort((a, b) => {
      return a[name] > b[name];
    });

    let sortedAlphabetically = {};

    for(var item in sorted) {
      const firstLetter = sorted[item].name[0];
      if(sortedAlphabetically[firstLetter]) {
        sortedAlphabetically[firstLetter].push(sorted[item]);
      } else {
        sortedAlphabetically[firstLetter] = [sorted[item]]; 
      }
    }

    console.log('sorted', sortedAlphabetically);
-4

A simple answer:

objArray.sort(function(obj1, obj2) {
   return obj1.DepartmentName > obj2.DepartmentName;
});

ES6 way:

objArray.sort((obj1, obj2) => {return obj1.DepartmentName > obj2.DepartmentName};

If you need to make it lowercase/uppercase etc, just do that and store that result in a variable than compare that variable. Example:

objArray.sort((obj1, obj2) => {
   var firstObj = obj1.toLowerCase();
   var secondObj = obj2.toLowerCase();
   return firstObj.DepartmentName > secondObj.DepartmentName;
});
Mar
  • 115
  • 1
  • 3
  • 12
-4

You have to pass a function that accepts two parameters, compares them, and returns a number, so assuming you wanted to sort them by ID you would write...

objArray.sort(function(a,b) {
    return a.id-b.id;
});
// objArray is now sorted by Id
zconnelly13
  • 272
  • 1
  • 4
  • 11