Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Say I have an array of JavaScript objects:

var objs = [ { first_nom: 'Lazslo',last_nom: 'Jamf' },
            { first_nom: 'Pig', last_nom: 'Bodine'  },
            { first_nom: 'Pirate', last_nom: 'Prentice' }
           ];

How can I sort them by the value of last_nom in JavaScript?

I know about sort(a,b), but that only seems to work on strings and numbers. Do I need to add a toString method to my objects?

share|improve this question
3  
Bonus points for names taken from Gravity's Rainbow. – Kris Giesing Jul 4 '12 at 19:07
Now, can anyone post how to do this via an insertion sort? – Raymond Jun 14 at 21:44
add comment (requires an account with 50 reputation)

9 Answers

up vote 246 down vote accepted

It's easy enough to write your own comparison function:

function compare(a,b) {
  if (a.last_nom < b.last_nom)
     return -1;
  if (a.last_nom > b.last_nom)
    return 1;
  return 0;
}

objs.sort(compare);
share|improve this answer
3  
thanks. Didn't know that sort took a function ref. – Tyrone Slothrop Jul 15 '09 at 3:45
45  
Or inline: objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} ); – Marco Demaio Feb 24 '10 at 18:29
1  
Nice example, worked perfectly – iancrowther Dec 5 '11 at 13:52
Thank you very much, but i'm struggling to see the logic that function follows... what is a & b? are they the 1st and 2nd, then 2nd,3rd then 3rd,4th?!? – Will Hancock Apr 19 '12 at 11:12
5  
show 2 more commentsadd comment (requires an account with 50 reputation)

You can also create a dynamic sort function that sorts objects by their value that you pass:

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1, property.length - 1);
    }
    return function (a,b) {
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

So you can have an array of objects like this:

var People = [
    {Name: "Name", Surname: "Surname"},
    {Name:"AAA", Surname:"ZZZ"},
    {Name: "Name", Surname: "AAA"}
];

...and it will work when you do:

People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));

I wouldn't recommend changing a native object prototype but just to give an example so you can implement it on your own objects (For the environments that support it, you can also use Object.defineProperty, which doesn't have the negative side-effects of fiddling with a native object's prototype):

Array.prototype.sortBy = function(property) {
    //Please don't just copy-paste this code.
    //See the explanation at the end. A lot could break.
    return this.sort(dynamicSort(property))
}

then you can do stuff like:

People.sortBy("Name");

Hope it helps.

You can use the function below to generate sort functions with multiple sort parameters.

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;
    }
}

Which would enable you to do something like this:

People.sort(dynamicSortMultiple("Name", "-Surname"));

(Implementation which is just below is inspired from Mike R's answer) *(my above comment on fiddling with a native object prototype still applies)*

Prototype implementation would be something like the following (Here's a working example):

//Don't just copy-paste this code. You will break the "for-in" loops
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Array.prototype.sortBy = function() {
        return this.sort(_dynamicSortMultiple.apply(null, arguments));
    }
}();

Please use this on your classes (functions) only (I can't stress this enough). If you're targeting IE v9.0 and up then, as I previously mentioned, use Object.defineProperty like this (working example):

//Won't work below IE9, but totally safe otherwise
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Object.defineProperty(Array.prototype, "sortBy", {
        enumerable: false,
        writable: true,
        value: function() {
            return this.sort(_dynamicSortMultiple.apply(null, arguments));
        }
    });
}();

All those prototype fun enables this:

People.sortBy("Name", "-Surname");

If you use the direct prototype access method (Object.defineProperty is fine) and other code does not check hasOwnProperty, kittens die! Ok, to be honest, no harm comes to any kitten really but probably things will break and every other developer in your team will hate you:

evil

See that last "SortBy"? Yeah. Not cool. Use Object.defineProperty where you can, and leave the Array.prototype alone otherwise.

share|improve this answer
4  
Love the dynamicSortMultiple! – Heather Dec 17 '12 at 23:43
Please note that property names in JavaScript can be any string and if you have properties starting with a "-" (extremely unlikely and probably not a good idea), you'll need to modify the dynamicSort function to use something else as a reverse sort indicator. – Ege Özcan Jan 10 at 15:18
this is awesome! – Damon Mar 7 at 20:26
thank you very much =) – Ege Özcan Mar 7 at 20:32
add comment (requires an account with 50 reputation)

If you have duplicate last names you might sort those by first name-

obj.sort(function(a,b){
  if(a.last_nom< b.last_nom) return -1;
  if(a.last_nom >b.last_nom) return 1;
  if(a.first_nom< b.first_nom) return -1;
  if(a.first_nom >b.first_nom) return 1;
  return 0;
}
share|improve this answer
Thanks, this was what I was looking for! – Sean Jul 9 '12 at 18:55
add comment (requires an account with 50 reputation)

Instead of using a custom comparison function, you could also create an object type with custom toString() method (which is invoked by the default comparison function):

function Person(firstName, lastName) {
    this.firtName = firstName;
    this.lastName = lastName;
}

Person.prototype.toString = function() {
    return this.lastName + ', ' + this.firstName;
}

var persons = [ new Person('Lazslo', 'Jamf'), ...]
persons.sort();
share|improve this answer
add comment (requires an account with 50 reputation)

underscore.js

use underscore, its small and awesome...

sortBy_.sortBy(list, iterator, [context]) Returns a sorted copy of list, ranked in ascending order by the results of running each value through iterator. Iterator may also be the string name of the property to sort by (eg. length).

var objs = [ { first_nom: 'Lazslo',last_nom: 'Jamf' },
        { first_nom: 'Pig', last_nom: 'Bodine'  },
        { first_nom: 'Pirate', last_nom: 'Prentice' }
       ];

_.sortBy( objs, 'first_nom' );
share|improve this answer
add comment (requires an account with 50 reputation)

additional desc params for Ege Özcan code

function dynamicSort(property, desc) {
    if (desc) {
        return function (a, b) {
            return (a[property] > b[property]) ? -1 : (a[property] < b[property]) ? 1 : 0;
        }   
    }
    return function (a, b) {
        return (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
    }
}
share|improve this answer
+1 for added dynamics, still simple – hubson bropa Oct 31 '12 at 21:20
add comment (requires an account with 50 reputation)

This script allows you to do just that unless you want to write your own comparison function or sorter:

http://www.thomasfrank.se/sorting_things.html

share|improve this answer
add comment (requires an account with 50 reputation)

Simple and quick solution to this problem using prototype inheritance.

Solution

Array.prototype.sortByProp = function(p){
 return this.sort(function(a,b){
  return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
 });
}

Example / Usage

objs = [{age:44,name:'vinay'},{age:24,name:'deepak'},{age:74,name:'suresh'}];

objs.sortByProp('age');
// Returns
// [{"age":24,"name":"deepak"},{"age":44,"name":"vinay"},{"age":74,"name":"suresh"}]

objs.sortByProp('name');
// Returns
// [{"age":24,"name":"deepak"},{"age":74,"name":"suresh"},{"age":44,"name":"vinay"}]
share|improve this answer
It dosn't just return another array. but actually sorts the original one!. – Vinay Aggarwal Jul 21 '12 at 5:43
add comment (requires an account with 50 reputation)

Combining Ege's dynamic solution with Vinay's idea, you get a nice robust solution:

Array.prototype.sortBy = function() {
    function _sortByAttr(attr) {
        var sortOrder = 1;
        if (attr[0] == "-") {
            sortOrder = -1;
            attr = attr.substr(1);
        }
        return function(a, b) {
            var result = (a[attr] < b[attr]) ? -1 : (a[attr] > b[attr]) ? 1 : 0;
            return result * sortOrder;
        }
    }
    function _getSortFunc() {
        if (arguments.length == 0) {
            throw "Zero length arguments not allowed for Array.sortBy()";
        }
        var args = arguments;
        return function(a, b) {
            for (var result = 0, i = 0; result == 0 && i < args.length; i++) {
                result = _sortByAttr(args[i])(a, b);
            }
            return result;
        }
    }
    return this.sort(_getSortFunc.apply(null, arguments));
}

Usage:

// Utility for printing objects
Array.prototype.print = function(title) {
    console.log("************************************************************************");
    console.log("**** "+title);
    console.log("************************************************************************");
    for (var i = 0; i < this.length; i++) {
        console.log("Name: "+this[i].FirstName, this[i].LastName, "Age: "+this[i].Age);
    }
}

// Setup sample data
var arrObj = [
    {FirstName: "Zach", LastName: "Emergency", Age: 35},
    {FirstName: "Nancy", LastName: "Nurse", Age: 27},
    {FirstName: "Ethel", LastName: "Emergency", Age: 42},
    {FirstName: "Nina", LastName: "Nurse", Age: 48},
    {FirstName: "Anthony", LastName: "Emergency", Age: 44},
    {FirstName: "Nina", LastName: "Nurse", Age: 32},
    {FirstName: "Ed", LastName: "Emergency", Age: 28},
    {FirstName: "Peter", LastName: "Physician", Age: 58},
    {FirstName: "Al", LastName: "Emergency", Age: 51},
    {FirstName: "Ruth", LastName: "Registration", Age: 62},
    {FirstName: "Ed", LastName: "Emergency", Age: 38},
    {FirstName: "Tammy", LastName: "Triage", Age: 29},
    {FirstName: "Alan", LastName: "Emergency", Age: 60},
    {FirstName: "Nina", LastName: "Nurse", Age: 54}
];

//Unit Tests
arrObj.sortBy("LastName").print("LastName Ascending");
arrObj.sortBy("-LastName").print("LastName Descending");
arrObj.sortBy("LastName", "FirstName", "-Age").print("LastName Ascending, FirstName Ascending, Age Descending");
arrObj.sortBy("-FirstName", "Age").print("FirstName Descending, Age Ascending");
arrObj.sortBy("-Age").print("Age Descending");
share|improve this answer
Thanks for the idea! By the way, please do not encourage people to change the Array Prototype (see the warning at the end of my example). – Ege Özcan May 10 at 14:51
add comment (requires an account with 50 reputation)

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.