4048

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?

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Tyrone Slothrop
  • 40,787
  • 3
  • 17
  • 8

60 Answers60

5601

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

Or inline (c/o Marco Demaio):

objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0))

Or simplified for numeric (c/o Andre Figueiredo):

objs.sort((a,b) => a.last_nom - b.last_nom); // b - a for reverse sort
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Wogan
  • 70,277
  • 5
  • 35
  • 35
  • 556
    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
  • 45
    Official docs: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort – mikemaccana May 18 '12 at 09:11
  • 295
    `return a.last_nom.localeCompare(b.last_nom)` will work, too. – Cerbrus Feb 14 '13 at 10:37
  • 196
    for those looking for a sort where the field is numeric, the compare function body: `return a.value - b.value;` (ASC) – Andre Figueiredo Jan 08 '14 at 12:06
  • 1
    You can convert a string to a number by using `charCodeAt`, then use the numeric inline above for a more concise one liner: `objs.sort((a,b) => a.last_nom.charCodeAt(0) - b.last_nom.charCodeAt(0));`. This avoids the ugly nested ternary. – tbatch Apr 07 '22 at 17:30
  • Wouldn't objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : -1) also do the trick for smaller arrays? I don't mind JS to swap objects if they're the same. – Herbert Van-Vliet Jul 14 '22 at 11:09
  • you should also test if "a" is null and then localCompare – serge Sep 30 '22 at 09:45
  • Or [Reusable](https://stackoverflow.com/a/65927668/1555615) : `const objectSorter = (p) => (a, b) => ((a, b) => a>b ? 1 : a – Marinos An Dec 15 '22 at 21:41
1085

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);
    }
    return function (a,b) {
        /* next line works with strings and numbers, 
         * and you may want to customize it to your needs
         */
        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"));

Actually this already answers the question. Below part is written because many people contacted me, complaining that it doesn't work with multiple parameters.

Multiple Parameters

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"));

Subclassing Array

For the lucky among us who can use ES6, which allows extending the native objects:

class MyArray extends Array {
    sortBy(...args) {
        return this.sort(dynamicSortMultiple(...args));
    }
}

That would enable this:

MyArray.from(People).sortBy("Name", "-Surname");
Ege Özcan
  • 13,971
  • 2
  • 30
  • 51
  • 10
    Nice. There is now a Typescript version of this answer: https://stackoverflow.com/a/68279093/8910547. Stay (type) safe! – Inigo Jul 07 '21 at 02:30
  • You shouldn't ever extend Array. – zero_cool Jul 01 '22 at 22:27
  • 4
    @zero_cool Array isn't being extended here (prototype stays the same), it's extended from. You indeed shouldn't change the prototype of native objects, but as I said, that's not what happens here. – Ege Özcan Jul 03 '22 at 06:58
  • not testing for null – serge Sep 30 '22 at 09:46
  • @serge any comparison of strings and nulls will result to false, putting null values at the end. if you change the a[property] < b[property] to a[property].localeCompare(b[property]), you can do a[property]?.localeCompare(b[property]) ?? 1 (take b as first if a has empty in property, and localeCompare will return -1 anyway if b has null at property -- illogical when both null though, so check for that as well maybe) – Ege Özcan Oct 01 '22 at 17:46
1017

In ES6/ES2015 or later you can do it this way:

objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));

Prior to ES6/ES2015

objs.sort(function(a, b) {
    return a.last_nom.localeCompare(b.last_nom)
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
  • If the values are numeric, you don't need `localeCompare`. You can use the standard `>` operator - like mentioned in the answer by @muasif80 - https://stackoverflow.com/a/67992215/6908282 – Gangula Oct 29 '21 at 19:29
243

Underscore.js

Use Underscore.js]. It’s 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' }
];

var sortedObjs = _.sortBy(objs, 'first_nom');
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Morrow
  • 8,965
  • 4
  • 29
  • 24
  • 23
    David, could you edit the answer to say, `var sortedObjs = _.sortBy( objs, 'first_nom' );`. `objs` will **not** be sorted itself as a result of this. The function will **return** a sorted array. That would make it more explicit. – Jess Jan 09 '14 at 04:01
  • 13
    To reverse sort: `var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();` – Erdal G. Jan 31 '16 at 10:43
  • 2
    you need to load the javascript libary "underscore": `` – and-bri May 29 '17 at 18:28
  • 7
    Also available in [`Lodash`](https://lodash.com/docs/4.17.5#sortBy) for the ones who prefer that one – WoJ Apr 17 '18 at 10:49
  • 7
    In lodash this would be the same: `var sortedObjs = _.sortBy( objs, 'first_nom' );` or if you want it in a different order: `var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );` – Travis Heeter Nov 15 '18 at 19:10
  • @TravisHeeter for lodash descending should be desc – Lys Jun 01 '22 at 16:05
139

Case sensitive

arr.sort((a, b) => a.name > b.name ? 1 : -1);

Case Insensitive

arr.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);

Useful Note

If no change in order (in case of the same strings) then the condition > will fail and -1 will be returned. But if strings are same then returning 1 or -1 will result in correct output

The other option could be to use >= operator instead of >


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


// Define a couple of sorting callback functions, one with hardcoded sort key and the other with an argument sort key
const sorter1 = (a, b) => a.last_nom.toLowerCase() > b.last_nom.toLowerCase() ? 1 : -1;
const sorter2 = (sortBy) => (a, b) => a[sortBy].toLowerCase() > b[sortBy].toLowerCase() ? 1 : -1;

objs.sort(sorter1);
console.log("Using sorter1 - Hardcoded sort property last_name", objs);

objs.sort(sorter2('first_nom'));
console.log("Using sorter2 - passed param sortBy='first_nom'", objs);

objs.sort(sorter2('last_nom'));
console.log("Using sorter2 - passed param sortBy='last_nom'", objs);
muasif80
  • 5,586
  • 4
  • 32
  • 45
  • The case sensitive approach is a good shorthand - especially if the values are numeric or date. – Gangula Oct 29 '21 at 19:26
  • TIP: if you'd like to reverse the order, you can simply swap `-1` and `1` for eg: from `1 : -1` to `-1 : 1` – Gangula Oct 29 '21 at 19:32
  • What about changing `(a, b) to (b, a)` :) – muasif80 Oct 30 '21 at 17:29
  • 2
    Yes, that works too. I just find swapping `1` & `-1` more straight forward and logical. – Gangula Oct 30 '21 at 18:03
  • @Gangula: It's also more easy to pickup on the reverse order of those, than of parameter names, but I suppose that comes with more experience reading code anyway (in case of maintenance). What I like best is the omission of another test to leave the items as be (return 0) in case they're the same... if the items are the same, I don't mind the swapping. – Herbert Van-Vliet Jul 22 '22 at 12:53
  • 1
    Returning non-zero for equal items violates the compareFn contract. The issue may not manifest in current implementations of sort(), but is not future-proof and/or may affect performance. – user3125367 Dec 22 '22 at 19:56
87

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;
});
BadFeelingAboutThis
  • 14,445
  • 2
  • 33
  • 40
kennebec
  • 102,654
  • 32
  • 106
  • 127
  • @BadFeelingAboutThis what does returning either -1 or 1 mean? I understand that -1 literally means that A is less than B just by the syntax, but why use a 1 or -1? I see everyone is using those numbers as return values, but why? Thanks. – Chris22 Aug 22 '18 at 06:15
  • 1
    @Chris22 a negative number returned means that `b` should come after `a` in the array. If a positive number is returned, it means `a` should come after `b`. If `0` is returned, it means they are considered equal. You can always read the documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort – BadFeelingAboutThis Aug 22 '18 at 16:54
  • 1
    @BadFeelingAboutThis thanks, for the explanation and the link. Believe it or not, I googled various snippets of code using the `1, 0, -1` before I asked this here. I just wasn't finding the info I needed. – Chris22 Aug 22 '18 at 17:07
71

As of 2018 there is a much shorter and elegant solution. Just use. Array.prototype.sort().

Example:

var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];

// sort by value
items.sort(function (a, b) {
  return a.value - b.value;
});
0leg
  • 13,464
  • 16
  • 70
  • 94
  • 20
    In the question strings were used for comparison as opposed to numbers. Your answer works great for sorting by numbers, but isn't so good for comparisons by string. – smcstewart Jun 18 '18 at 10:02
  • The `a.value - b.value` used to compare the object's attributes (**numbers** in this case) can be adopted for the various times of data. For example, regex can be used to compare each pair of the neighboring **strings**. – 0leg Mar 26 '19 at 09:10
  • This implementation is quite good if you need to sort it by ID. Yeah , you have suggested to use regex to compare neighbouring string which makes solution more complicated whereas purpose of this simpliefied version will be otherwise if regex is used along with given solution. Simplicity is the best. – surendrapanday Dec 10 '19 at 08:29
69

Simple and quick solution to this problem using prototype inheritance:

Array.prototype.sortBy = function(p) {
  return this.slice(0).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.sortBy('age');
// Returns
// [{"age":24,"name":"deepak"},{"age":44,"name":"vinay"},{"age":74,"name":"suresh"}]

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

Update: No longer modifies original array.

Web_Designer
  • 72,308
  • 93
  • 206
  • 262
Vinay Aggarwal
  • 1,565
  • 1
  • 10
  • 19
  • 8
    It dosn't just return another array. but actually sorts the original one!. – Vinay Aggarwal Jul 21 '12 at 05:43
  • 1
    If you want to make sure you are using a natural sort with numbers (i.e., 0,1,2,10,11 etc...) use parseInt with the Radix set. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt so: return (parseInt(a[p],10) > parseInt(b[p],10)) ? 1 : (parseInt(a[p],10) < parseInt(b[p],10)) ? -1 : 0; – Paul May 11 '15 at 19:14
  • @codehuntr Thanks for correcting it. but i guess instead of making sort function to do this sensitization, it's better if we make a separate function to fix data types. Because sort function can't not tell which property will contain what kind of data. :) – Vinay Aggarwal May 21 '15 at 16:55
  • 1
    i think this will only work on some prop types.. you'd want to add date/string handling etc.. i.e. if type is string use return a.localCompare(b) etc. etc.. – Sonic Soul Nov 09 '21 at 14:41
  • I assume the purpose of `.slice(0)` is to make a shallow copy of the array. – emallove Jan 06 '23 at 19:05
65

Old answer that is not correct:

arr.sort((a, b) => a.name > b.name)

UPDATE

From Beauchamp's comment:

arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))

More readable format:

arr.sort((a, b) => {
  if (a.name < b.name) return -1
  return a.name > b.name ? 1 : 0
})

Without nested ternaries:

arr.sort((a, b) => a.name < b.name ? - 1 : Number(a.name > b.name))

Explanation: Number() will cast true to 1 and false to 0.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Damjan Pavlica
  • 31,277
  • 10
  • 71
  • 76
44

Lodash (a superset of Underscore.js).

It's good not to add a framework for every simple piece of logic, but relying on well tested utility frameworks can speed up development and reduce the amount of bugs.

Lodash produces very clean code and promotes a more functional programming style. In one glimpse, it becomes clear what the intent of the code is.

The OP's issue can simply be solved as:

const sortedObjs = _.sortBy(objs, 'last_nom');

More information? For example, we have the following nested object:

const users = [
  { 'user': {'name':'fred', 'age': 48}},
  { 'user': {'name':'barney', 'age': 36 }},
  { 'user': {'name':'wilma'}},
  { 'user': {'name':'betty', 'age': 32}}
];

We now can use the _.property shorthand user.age to specify the path to the property that should be matched. We will sort the user objects by the nested age property. Yes, it allows for nested property matching!

const sortedObjs = _.sortBy(users, ['user.age']);

Want it reversed? No problem. Use _.reverse.

const sortedObjs = _.reverse(_.sortBy(users, ['user.age']));

Want to combine both using chain?

const { chain } = require('lodash');
const sortedObjs = chain(users).sortBy('user.age').reverse().value();

Or when do you prefer flow over chain?

const { flow, reverse, sortBy } = require('lodash/fp');
const sortedObjs = flow([sortBy('user.age'), reverse])(users);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nico Van Belle
  • 4,911
  • 4
  • 32
  • 49
43

You can use Easiest Way: Lodash

(https://lodash.com/docs/4.17.10#orderBy)

This method is like _.sortBy except that it allows specifying the sort orders of the iteratees to sort by. If orders is unspecified, all values are sorted in ascending order. Otherwise, specify an order of "desc" for descending or "asc" for ascending sort order of corresponding values.

Arguments

collection (Array|Object): The collection to iterate over. [iteratees=[_.identity]] (Array[]|Function[]|Object[]|string[]): The iteratees to sort by. [orders] (string[]): The sort orders of iteratees.

Returns

(Array): Returns the new sorted array.


var _ = require('lodash');
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"}
    ];
    
_.orderBy(homes, ['city', 'state', 'zip'], ['asc', 'desc', 'asc']);
turivishal
  • 34,368
  • 7
  • 36
  • 59
Harshal Yeole
  • 4,812
  • 1
  • 21
  • 43
41

I haven't seen this particular approach suggested, so here's a terse comparison method I like to use that works for both string and number types:

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

const sortBy = fn => {
  const cmp = (a, b) => -(a < b) || +(a > b);
  return (a, b) => cmp(fn(a), fn(b));
};

const getLastName = o => o.last_nom;
const sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));

Explanation of sortBy()

sortBy() accepts a fn that selects a value from an object to use in comparison, and returns a function that can be passed to Array.prototype.sort(). In this example, we're comparing o.last_nom. Whenever we receive two objects such as

a = { first_nom: 'Lazslo', last_nom: 'Jamf' }
b = { first_nom: 'Pig', last_nom: 'Bodine' }

we compare them with (a, b) => cmp(fn(a), fn(b)). Given that

fn = o => o.last_nom

we can expand the comparison function to (a, b) => cmp(a.last_nom, b.last_nom). Because of the way logical OR (||) works in JavaScript, cmp(a.last_nom, b.last_nom) is equivalent to

if (a.last_nom < b.last_nom) return -1;
if (a.last_nom > b.last_nom) return 1;
return 0;

Incidentally, this is called the three-way comparison "spaceship" (<=>) operator in other languages.

Finally, here's the ES5-compatible syntax without using arrow functions:

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

function sortBy(fn) {
  function cmp(a, b) { return -(a < b) || +(a > b); }
  return function (a, b) { return cmp(fn(a), fn(b)); };
}

function getLastName(o) { return o.last_nom; }
var sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • I like this approach but I think using the shorthand of `-(fa < fb) || +(fa > fb)` is a mistake here. That's multiple statements being condensed into one line of code. The alternative, written with an `if` statement, would be much more readable whilst still being fairly concise. I think it's a mistake to sacrifice readability for prettiness. – MSOACC Jul 09 '20 at 11:02
  • @MSOACC thanks for your opinion but I respectfully disagree. Other languages implement a [three-way comparison operator](https://stackoverflow.com/a/30365399/1541563) that performs the same comparison, so just think of it conceptually as `fa <=> fb`. – Patrick Roberts Jul 09 '20 at 14:55
  • Hey Patrick, I like your answer but it's would work properly only with English characters (`const cmp = (a, b) => -(a < b) || +(a > b);`) Think of `["ä", "a", "c", "b"].sort(cmp)` => `["a", "b", "c", "ä"]`, where `ä` is pushed to the end. Instead you should probably update the comparison function to: `const cmp = (a, b) => a.localeCompare(b);` => `["a", "ä", "b", "c"]` Cheers and thanks for the answer ;-) – rjanjic Oct 09 '20 at 13:48
  • @rjanjic thanks for the feedback. I'm aware that it sorts based on the code point of the character in unicode. However changing it to use `localeCompare` removes the ability to sort numbers, and is also _significantly_ slower. – Patrick Roberts Oct 09 '20 at 19:22
36

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();
Christoph
  • 164,997
  • 36
  • 182
  • 240
33

There are many good answers here, but I would like to point out that they can be extended very simply to achieve a lot more complex sorting. The only thing you have to do is to use the OR operator to chain comparison functions like this:

objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )

Where fn1, fn2, ... are the sort functions which return [-1,0,1]. This results in "sorting by fn1" and "sorting by fn2" which is pretty much equal to ORDER BY in SQL.

This solution is based on the behaviour of || operator which evaluates to the first evaluated expression which can be converted to true.

The simplest form has only one inlined function like this:

// ORDER BY last_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )

Having two steps with last_nom,first_nom sort order would look like this:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) ||
                  a.first_nom.localeCompare(b.first_nom)  )

A generic comparison function could be something like this:

// ORDER BY <n>
let cmp = (a,b,n)=>a[n].localeCompare(b[n])

This function could be extended to support numeric fields, case-sensitivity, arbitrary data types, etc.

You can use them by chaining them by sort priority:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> cmp(a,b, "last_nom") || cmp(a,b, "first_nom") )
// ORDER_BY last_nom, first_nom DESC
objs.sort((a,b)=> cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )
// ORDER_BY last_nom DESC, first_nom DESC
objs.sort((a,b)=> -cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )

The point here is that pure JavaScript with functional approach can take you a long way without external libraries or complex code. It is also very effective, since no string parsing have to be done.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tero Tolonen
  • 4,144
  • 4
  • 27
  • 32
32

Try this:

Up to ES5
// Ascending sort
items.sort(function (a, b) {
   return a.value - b.value;
});


// Descending sort
items.sort(function (a, b) {
   return b.value - a.value;
});
In ES6 and above
// Ascending sort
items.sort((a, b) => a.value - b.value);

// Descending sort
items.sort((a, b) => b.value - a.value);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Abhishek
  • 583
  • 5
  • 16
30

Use JavaScript sort method

The sort method can be modified to sort anything like an array of numbers, strings and even objects using a compare function.

A compare function is passed as an optional argument to the sort method.

This compare function accepts 2 arguments generally called a and b. Based on these 2 arguments you can modify the sort method to work as you want.

  1. If the compare function returns less than 0, then the sort() method sorts a at a lower index than b. Simply a will come before b.
  2. If the compare function returns equal to 0, then the sort() method leaves the element positions as they are.
  3. If the compare function returns greater than 0, then the sort() method sorts a at greater index than b. Simply a will come after b.

Use the above concept to apply on your object where a will be your object property.

var objs = [
  { first_nom: 'Lazslo', last_nom: 'Jamf' },
  { first_nom: 'Pig', last_nom: 'Bodine' },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];
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);
console.log(objs)
// for better look use console.table(objs)
output
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Satish Chandra Gupta
  • 2,970
  • 1
  • 22
  • 22
29

Example Usage:

objs.sort(sortBy('last_nom'));

Script:

/**
 * @description
 * Returns a function which will sort an
 * array of objects by the given key.
 *
 * @param  {String}  key
 * @param  {Boolean} reverse
 * @return {Function}
 */
const sortBy = (key, reverse) => {

  // Move smaller items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveSmaller = reverse ? 1 : -1;

  // Move larger items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveLarger = reverse ? -1 : 1;

  /**
   * @param  {*} a
   * @param  {*} b
   * @return {Number}
   */
  return (a, b) => {
    if (a[key] < b[key]) {
      return moveSmaller;
    }
    if (a[key] > b[key]) {
      return moveLarger;
    }
    return 0;
  };
};
Jamie Mason
  • 4,159
  • 2
  • 32
  • 42
  • thank you for breaking this down, I am trying to understand why digits `1, 0, -1` are used for sort ordering. Even with your explanation above, which looks very good-- I'm still not quite understanding it. I always think of `-1` as when using array length property, i.e.: `arr.length = -1` means that the item isn't found. I'm probably mixing things up here, but could you help me understand why digits `1, 0, -1` are used to determine order? Thanks. – Chris22 Aug 22 '18 at 06:23
  • 1
    This isn't _entirely_ accurate but it might help to think of it like this: the function passed to array.sort is called once for each item in the array, as the argument named "a". The return value of each function call is how the index (current position number) of item "a" should be altered compared to the next item "b". The index dictates the order of the array (0, 1, 2 etc) So if "a" is at index 5 and you return -1 then 5 + -1 == 4 (move it nearer front) 5 + 0 == 5 (keep it where it is) etc. It walks the array comparing 2 neighbours each time until it reaches the end, leaving a sorted array. – Jamie Mason Aug 23 '18 at 09:09
  • 1
    thank you for taking the time to explain this further. So using your explanation and the [MDN Array.prototype.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort), I'll tell you what I'm thinking of this: in comparison to `a` and `b`, if `a` is greater than `b` add 1 to the index of `a` and place it behind `b`, if `a` is less than `b`, subtract 1 from `a` and place it in front of `b`. If `a` and `b` are the same, add 0 to `a` and leave it where it is. – Chris22 Aug 23 '18 at 16:28
27

Write short code:

objs.sort((a, b) => a.last_nom > b.last_nom ? 1 : -1)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
artem
  • 457
  • 4
  • 5
25

I didn't see any implementation similar to mine. This version is based on the Schwartzian transform idiom.

function sortByAttribute(array, ...attrs) {
  // Generate an array of predicate-objects containing
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // Schwartzian transform idiom implementation. AKA "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i])
        result = -1;
      if (o1.compareValues[i] > o2.compareValues[i])
        result = 1;
      if (result *= predicates[i].descend)
        break;
    }
    return result;
  })
  .map(item => item.src);
}

Here's an example how to use it:

let games = [
  { name: 'Mashraki',          rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// Sort by one attribute
console.log(sortByAttribute(games, 'name'));
// Sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
a8m
  • 9,334
  • 4
  • 37
  • 40
18

Sorting (more) Complex Arrays of Objects

Since you probably encounter more complex data structures like this array, I would expand the solution.

TL;DR

Are more pluggable version based on @ege-Özcan's very lovely answer.

Problem

I encountered the below and couldn't change it. I also did not want to flatten the object temporarily. Nor did I want to use underscore / lodash, mainly for performance reasons and the fun to implement it myself.

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

Goal

The goal is to sort it primarily by People.Name.name and secondarily by People.Name.surname

Obstacles

Now, in the base solution uses bracket notation to compute the properties to sort for dynamically. Here, though, we would have to construct the bracket notation dynamically also, since you would expect some like People['Name.name'] would work - which doesn't.

Simply doing People['Name']['name'], on the other hand, is static and only allows you to go down the n-th level.

Solution

The main addition here will be to walk down the object tree and determine the value of the last leaf, you have to specify, as well as any intermediary leaf.

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

People.sort(dynamicMultiSort(['Name','name'], ['Name', '-surname']));
// Results in...
// [ { Name: { name: 'AAA', surname: 'ZZZ' }, Middlename: 'Abrams' },
//   { Name: { name: 'Name', surname: 'Surname' }, Middlename: 'JJ' },
//   { Name: { name: 'Name', surname: 'AAA' }, Middlename: 'Wars' } ]

// same logic as above, but strong deviation for dynamic properties 
function dynamicSort(properties) {
  var sortOrder = 1;
  // determine sort order by checking sign of last element of array
  if(properties[properties.length - 1][0] === "-") {
    sortOrder = -1;
    // Chop off sign
    properties[properties.length - 1] = properties[properties.length - 1].substr(1);
  }
  return function (a,b) {
    propertyOfA = recurseObjProp(a, properties)
    propertyOfB = recurseObjProp(b, properties)
    var result = (propertyOfA < propertyOfB) ? -1 : (propertyOfA > propertyOfB) ? 1 : 0;
    return result * sortOrder;
  };
}

/**
 * Takes an object and recurses down the tree to a target leaf and returns it value
 * @param  {Object} root - Object to be traversed.
 * @param  {Array} leafs - Array of downwards traversal. To access the value: {parent:{ child: 'value'}} -> ['parent','child']
 * @param  {Number} index - Must not be set, since it is implicit.
 * @return {String|Number}       The property, which is to be compared by sort.
 */
function recurseObjProp(root, leafs, index) {
  index ? index : index = 0
  var upper = root
  // walk down one level
  lower = upper[leafs[index]]
  // Check if last leaf has been hit by having gone one step too far.
  // If so, return result from last step.
  if (!lower) {
    return upper
  }
  // Else: recurse!
  index++
  // HINT: Bug was here, for not explicitly returning function
  // https://stackoverflow.com/a/17528613/3580261
  return recurseObjProp(lower, leafs, index)
}

/**
 * Multi-sort your array by a set of properties
 * @param {...Array} Arrays to access values in the form of: {parent:{ child: 'value'}} -> ['parent','child']
 * @return {Number} Number - number for sort algorithm
 */
function dynamicMultiSort() {
  var args = Array.prototype.slice.call(arguments); // slight deviation to base

  return function (a, b) {
    var i = 0, result = 0, numberOfProperties = args.length;
    // REVIEW: slightly verbose; maybe no way around because of `.sort`-'s nature
    // Consider: `.forEach()`
    while(result === 0 && i < numberOfProperties) {
      result = dynamicSort(args[i])(a, b);
      i++;
    }
    return result;
  }
}

Example

Working example on JSBin

Community
  • 1
  • 1
eljefedelrodeodeljefe
  • 6,304
  • 7
  • 29
  • 61
  • 3
    Why? This is not the answer to original question and "the goal" could be solved simply with People.sort((a,b)=>{ return a.Name.name.localeCompare(b.Name.name) || a.Name.surname.localeCompare(b.Name.surname) }) – Tero Tolonen May 03 '16 at 16:02
16

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");
Not A Bot
  • 2,474
  • 2
  • 16
  • 33
Mike R
  • 231
  • 2
  • 7
  • 1
    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 '13 at 14:51
15

One more option:

var someArray = [...];

function generateSortFn(prop, reverse) {
    return function (a, b) {
        if (a[prop] < b[prop]) return reverse ? 1 : -1;
        if (a[prop] > b[prop]) return reverse ? -1 : 1;
        return 0;
    };
}

someArray.sort(generateSortFn('name', true));

It sorts ascending by default.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ravshansbox
  • 748
  • 11
  • 21
  • 1
    Slightly changed version for sorting by multiple fields is here if needed: http://stackoverflow.com/questions/6913512/how-to-sort-an-array-of-objects-by-multiple-fields/38037580#38037580 – ravshansbox Dec 01 '16 at 12:37
  • it looks like it could be the next one: export function generateSortFn( prop: string, reverse: boolean = false ): (...args: any) => number { return (a, b) => { return a[prop] < b[prop] ? reverse ? 1 : -1 : a[prop] > b[prop] ? reverse ? -1 : 1 : 0; }; } – Den Kerny May 23 '20 at 04:29
  • agreed, but in some case i haven't got needs to look at utility functions. – Den Kerny May 25 '20 at 02:00
13

A simple way:

objs.sort(function(a,b) {
  return b.last_nom.toLowerCase() < a.last_nom.toLowerCase();
});

See that '.toLowerCase()' is necessary to prevent erros in comparing strings.

Caio Ladislau
  • 1,257
  • 13
  • 25
  • 5
    You could use [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) to let the code a little more elegant: `objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );` – Sertage May 24 '17 at 15:04
  • This is wrong for the same reason as explained [here](https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value-in-javascript#comment35528003_21341006). – Patrick Roberts Jul 18 '18 at 09:24
  • 2
    Arrow functions are not ES5-worthy. Tons of engines still are restricted to ES5. In my case, I find the answer above significantly better since I'm on an ES5 engine (forced by my company) – dylanh724 Jul 21 '18 at 08:50
13

Warning!
Using this solution is not recommended as it does not result in a sorted array. It is being left here for future reference, because the idea is not rare.

objs.sort(function(a,b){return b.last_nom>a.last_nom})
Ben Carp
  • 24,214
  • 9
  • 60
  • 72
Roshni Bokade
  • 343
  • 2
  • 9
13

This is my take on this:

The order parameter is optional and defaults to "ASC" for ascending order.

It works on accented characters and it's case insensitive.

Note: It sorts and returns the original array.

function sanitizeToSort(str) {
  return str
    .normalize('NFD')                   // Remove accented and diacritics
    .replace(/[\u0300-\u036f]/g, '')    // Remove accented and diacritics
    .toLowerCase()                      // Sort will be case insensitive
  ;
}

function sortByProperty(arr, property, order="ASC") {
  arr.forEach((item) => item.tempProp = sanitizeToSort(item[property]));
  arr.sort((a, b) => order === "ASC" ?
      a.tempProp > b.tempProp ?  1 : a.tempProp < b.tempProp ? -1 : 0
    : a.tempProp > b.tempProp ? -1 : a.tempProp < b.tempProp ?  1 : 0
  );
  arr.forEach((item) => delete item.tempProp);
  return arr;
}

Snippet

function sanitizeToSort(str) {
  return str
    .normalize('NFD')                   // Remove accented characters
    .replace(/[\u0300-\u036f]/g, '')    // Remove diacritics
    .toLowerCase()
  ;
}

function sortByProperty(arr, property, order="ASC") {
  arr.forEach((item) => item.tempProp = sanitizeToSort(item[property]));
  arr.sort((a, b) => order === "ASC" ?
      a.tempProp > b.tempProp ?  1 : a.tempProp < b.tempProp ? -1 : 0
    : a.tempProp > b.tempProp ? -1 : a.tempProp < b.tempProp ?  1 : 0
  );
  arr.forEach((item) => delete item.tempProp);
  return arr;
}

const rockStars = [
  { name: "Axl",
    lastname: "Rose" },
  { name: "Elthon",
    lastname: "John" },
  { name: "Paul",
    lastname: "McCartney" },
  { name: "Lou",
    lastname: "Reed" },
  { name: "freddie",             // Works on lower/upper case
    lastname: "mercury" },
  { name: "Ámy",                 // Works on accented characters too
    lastname: "winehouse"}

];

sortByProperty(rockStars, "name");

console.log("Ordered by name A-Z:");
rockStars.forEach((item) => console.log(item.name + " " + item.lastname));

sortByProperty(rockStars, "lastname", "DESC");

console.log("\nOrdered by lastname Z-A:");
rockStars.forEach((item) => console.log(item.lastname + ", " + item.name));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cbdeveloper
  • 27,898
  • 37
  • 155
  • 336
12

Additional desc parameters for Ege Özcan's 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;
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Behnam Shomali
  • 863
  • 1
  • 12
  • 14
12

Given the original example:

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

Sort by multiple fields:

objs.sort(function(left, right) {
    var last_nom_order = left.last_nom.localeCompare(right.last_nom);
    var first_nom_order = left.first_nom.localeCompare(right.first_nom);
    return last_nom_order || first_nom_order;
});

Notes

  • a.localeCompare(b) is universally supported and returns -1,0,1 if a<b,a==b,a>b respectively.
  • || in the last line gives last_nom priority over first_nom.
  • Subtraction works on numeric fields: var age_order = left.age - right.age;
  • Negate to reverse order, return -last_nom_order || -first_nom_order || -age_order;
Bob Stein
  • 16,271
  • 10
  • 88
  • 101
12

A simple function that sorts an array of object by a property:

function sortArray(array, property, direction) {
    direction = direction || 1;
    array.sort(function compare(a, b) {
        let comparison = 0;
        if (a[property] > b[property]) {
            comparison = 1 * direction;
        } else if (a[property] < b[property]) {
            comparison = -1 * direction;
        }
        return comparison;
    });
    return array; // Chainable
}

Usage:

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

sortArray(objs, "last_nom"); // Asc
sortArray(objs, "last_nom", -1); // Desc
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Francois Girard
  • 348
  • 3
  • 15
12

Sorting objects with Intl.Collator for the specific case when you want natural sorting (i.e. 1,2,10,11,111).

const files = [
 {name: "1.mp3", size: 123},
 {name: "10.mp3", size: 456},
 {name: "100.mp3", size: 789},
 {name: "11.mp3", size: 123},
 {name: "111.mp3", size: 456},
 {name: "2.mp3", size: 789},
];

const naturalCollator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});

files.sort((a, b) => naturalCollator.compare(a.name, b.name));

console.log(files);

Note: the undefined constructor argument for Intl.Collator represents the locale, which can be an explicit ISO 639-1 language code such as en, or the system default locale when undefined.

Browser support for Intl.Collator

ccpizza
  • 28,968
  • 18
  • 162
  • 169
10

You may need to convert them to the lowercase form in order to prevent from confusion.

objs.sort(function (a, b) {

    var nameA = a.last_nom.toLowerCase(), nameB = b.last_nom.toLowerCase()

    if (nameA < nameB)
      return -1;
    if (nameA > nameB)
      return 1;
    return 0;  // No sorting
})
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Burak Keceli
  • 933
  • 1
  • 15
  • 31
10

Using Ramda,

npm install ramda

import R from 'ramda'
var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];
var ascendingSortedObjs = R.sortBy(R.prop('last_nom'), objs)
var descendingSortedObjs = R.reverse(ascendingSortedObjs)
Sridhar Sg
  • 1,546
  • 13
  • 21
  • What is Ramda? Can you [add](https://stackoverflow.com/posts/44919961/edit) a reference to it (e.g., a (non-naked) link)? (But ******* ***without*** ******* "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Dec 10 '22 at 21:27
9
function compare(propName) {
    return function(a,b) {
        if (a[propName] < b[propName])
            return -1;
        if (a[propName] > b[propName])
            return 1;
        return 0;
    };
}

objs.sort(compare("last_nom"));
Evgenii
  • 3,283
  • 3
  • 27
  • 32
  • 2
    Please consider editing your post to add more explanation about what your code does and why it will solve the problem. An answer that mostly just contains code (even if it's working) usually wont help the OP to understand their problem. – Drenmi Oct 29 '15 at 18:16
9

You can use a reusable sort function.

Array.prototype.order = function (prop, methods = {}) {
    if (prop?.constructor == Object) {
        methods = prop;
        prop = null;
    }
    const [orderType_a, orderType_b] = methods.reverse ? [1, -1] : [-1, 1];

    const $ = x => prop
        ? methods.insensitive
            ? String(x[prop]).toLowerCase()
            : x[prop]
        : methods.insensitive
            ? String(x).toLowerCase()
            : x;

    const fn = (a, b) => $(a) < $(b) ? orderType_a : $(b) < $(a) ? orderType_b : 0;
    return this.sort(fn);
};

It can be used to sort both arrays and objects in the array.

let items = [{ x: "Z" }, 3, "1", "0", 2, { x: "a" }, { x: 0 }];
items
    .order("x", { insensitive: 1 })
    // [ { x: 0 }, { x: 'a' }, 3, '1', '0', 2, { x: 'Z' } ]
    .order({ reverse: 1 })
    // [ { x: 0 }, { x: 'a' }, 3, 2, { x: 'Z' }, '1', '0' ]
    .sort(x => typeof x == "string" || typeof x == "number" ? -1 : 0)
    // [ '0', '1', 2, 3, { x: 0 }, { x: 'a' }, { x: 'Z' } ]

1nd (optional) > to sort object contain in array.
2rd is method > { reverse: any, insensitive: any }

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nur
  • 2,361
  • 2
  • 16
  • 34
8

Acording your example, you need to sort by two fields (last name, first name), rather than one. You can use the Alasql library to make this sort in one line:

var res = alasql('SELECT * FROM ? ORDER BY last_nom, first_nom',[objs]);

Try this example at JSFiddle.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
agershun
  • 4,077
  • 38
  • 41
8

This is a simple problem. I don't know why people have such complex solutions.

A simple sort function (based on the quicksort algorithm):

function sortObjectsArray(objectsArray, sortKey)
{
    // Quick Sort:
    var retVal;

    if (1 < objectsArray.length)
    {
        var pivotIndex = Math.floor((objectsArray.length - 1) / 2);  // Middle index
        var pivotItem = objectsArray[pivotIndex];                    // Value in the middle index
        var less = [], more = [];

        objectsArray.splice(pivotIndex, 1);                          // Remove the item in the pivot position
        objectsArray.forEach(function(value, index, array)
        {
            value[sortKey] <= pivotItem[sortKey] ?                   // Compare the 'sortKey' proiperty
                less.push(value) :
                more.push(value) ;
        });

        retVal = sortObjectsArray(less, sortKey).concat([pivotItem], sortObjectsArray(more, sortKey));
    }
    else
    {
        retVal = objectsArray;
    }

    return retVal;
}

Use example:

var myArr =
        [
            { val: 'x', idx: 3 },
            { val: 'y', idx: 2 },
            { val: 'z', idx: 5 },
        ];

myArr = sortObjectsArray(myArr, 'idx');
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gil Epshtain
  • 8,670
  • 7
  • 63
  • 89
  • 6
    How is implementing quick sort in js a simple solution? Simple algorithm but not a simple solution. – Andrew Nov 23 '15 at 22:46
  • It simple since it don't use any outer libraries and it don't change the object's prototype. In my opinion, the length of the code don't have direct impact on the code's complexity – Gil Epshtain Nov 24 '15 at 12:02
  • 3
    Well, let me try with different words: How reinventing the wheel is a simple solution? – Roberto14 Dec 09 '15 at 17:36
7

Way 1:

You can use Underscore.js. Import underscore first.

 import * as _ from 'underscore';
 let SortedObjs = _.sortBy(objs, 'last_nom');

Way 2: Use a compare function.

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

objs.sort(compare);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Harun Or Rashid
  • 5,589
  • 1
  • 19
  • 21
7

Simple answer:

objs.sort((a,b)=>a.last_nom.localeCompare(b.last_nom))

Details:

Today it is very simple, You can compare strings with localeCompare. As the Mozilla Doc says:

The localeCompare() method returns a number indicating whether a reference string comes before, or after, or is the same as the given string in sort order.

    //example1:
    console.log("aaa".localeCompare("aab")); //-1
    console.log("aaa".localeCompare("aaa")); //0
    console.log("aab".localeCompare("aaa")); //1

    //example2:
    const a = 'réservé'; // with accents, lowercase
    const b = 'RESERVE'; // no accents, uppercase

    console.log(a.localeCompare(b));
    // expected output: 1
    console.log(a.localeCompare(b, 'en', { sensitivity: 'base' }));
    // expected output: 0

For more details see Mozilla doclocaleCompare:

SeyyedKhandon
  • 5,197
  • 8
  • 37
  • 68
6
let propName = 'last_nom';

let sorted_obj = objs.sort((a,b) => {
    if(a[propName] > b[propName]) {
        return 1;
    }
    if (a[propName] < b[propName]) {
        return -1;
    }
    return 0;
}

//This works because the js built-in sort function allows us to define our
//own way of sorting, this funny looking function is simply telling `sort` how to
//determine what is larger. 
//We can use `if(a[propName] > b[propName])` because string comparison is already built into JS
//if you try console.log('a' > 'z' ? 'a' : 'z')
//the output will be 'z' as 'a' is not greater than 'z'
//The return values 0,-1,1 are how we tell JS what to sort on. We're sorting on the last_nom property of the object. 
//When sorting a list it comes down to comparing two items and how to determine which one of them is "larger". 
//We need a way to tell JS how to determine which one is larger. 
//The sort defining function will use the case that returns a 1 to mean that a > b
//and the case that returns -1 to mean that a < b

DonCarleone
  • 544
  • 11
  • 20
  • 1
    can you briefly describe the return values 1, -1, 0 a bit? – stuckoverflow Nov 22 '22 at 12:24
  • 1
    It is how we tell JS what to sort on. We're sorting on the `last_nom` property of the object. When sorting a list it comes down to comparing two items and how to determine which one of them is "larger". We need a way to tell JS how to determine which one is larger. The sort defining function will use the case that returns a 1 to mean that a > b and the case that returns -1 is the case that a < b. – DonCarleone Nov 22 '22 at 16:25
  • 1
    Can you [add](https://stackoverflow.com/posts/73708402/edit) it to the answer (comments may be deleted at any time)? But ********************** ***without*** ********************** "Edit:", "Update:", or similar - the answer should appear as if it was written today. – Peter Mortensen Dec 10 '22 at 19:13
  • @stuckoverflow See also [Intl.Collator.prototype.compare() > Description](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/compare#description): "_The `compare` getter function returns a number indicating how `string1` and `string2` compare to each other according to the sort order of this [`Intl.Collator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator) object: a negative value if `string1` comes before `string2`; a positive value if `string1` comes after `string2`; 0 if they are considered equal._". – Gerold Broser Jun 11 '23 at 00:19
5

Using xPrototype's sortBy:

var o = [
  { Name: 'Lazslo', LastName: 'Jamf'     },
  { Name: 'Pig',    LastName: 'Bodine'   },
  { Name: 'Pirate', LastName: 'Prentice' },
  { Name: 'Pag',    LastName: 'Bodine'   }
];


// Original
o.each(function (a, b) { console.log(a, b); });
/*
 0 Object {Name: "Lazslo", LastName: "Jamf"}
 1 Object {Name: "Pig", LastName: "Bodine"}
 2 Object {Name: "Pirate", LastName: "Prentice"}
 3 Object {Name: "Pag", LastName: "Bodine"}
*/


// Sort By LastName ASC, Name ASC
o.sortBy('LastName', 'Name').each(function(a, b) { console.log(a, b); });
/*
 0 Object {Name: "Pag", LastName: "Bodine"}
 1 Object {Name: "Pig", LastName: "Bodine"}
 2 Object {Name: "Lazslo", LastName: "Jamf"}
 3 Object {Name: "Pirate", LastName: "Prentice"}
*/


// Sort by LastName ASC and Name ASC
o.sortBy('LastName'.asc, 'Name'.asc).each(function(a, b) { console.log(a, b); });
/*
 0 Object {Name: "Pag", LastName: "Bodine"}
 1 Object {Name: "Pig", LastName: "Bodine"}
 2 Object {Name: "Lazslo", LastName: "Jamf"}
 3 Object {Name: "Pirate", LastName: "Prentice"}
*/


// Sort by LastName DESC and Name DESC
o.sortBy('LastName'.desc, 'Name'.desc).each(function(a, b) { console.log(a, b); });
/*
 0 Object {Name: "Pirate", LastName: "Prentice"}
 1 Object {Name: "Lazslo", LastName: "Jamf"}
 2 Object {Name: "Pig", LastName: "Bodine"}
 3 Object {Name: "Pag", LastName: "Bodine"}
*/


// Sort by LastName DESC and Name ASC
o.sortBy('LastName'.desc, 'Name'.asc).each(function(a, b) { console.log(a, b); });
/*
 0 Object {Name: "Pirate", LastName: "Prentice"}
 1 Object {Name: "Lazslo", LastName: "Jamf"}
 2 Object {Name: "Pag", LastName: "Bodine"}
 3 Object {Name: "Pig", LastName: "Bodine"}
*/
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
5

Sort an array of objects

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

// Property to sort by
var args = "last_nom";

// Function to sort the data by the given property
function sortByProperty(property) {
    return function (a, b) {
        var sortStatus = 0,
            aProp = a[property].toLowerCase(),
            bProp = b[property].toLowerCase();
        if (aProp < bProp) {
            sortStatus = -1;
        } else if (aProp > bProp) {
            sortStatus = 1;
        }
        return sortStatus;
    };
}

// Implementation
var sortedArray = booksArray.sort(sortByProperty(args));

console.log("sortedArray: " + JSON.stringify(sortedArray) );

Console log output:

"sortedArray: 
[{"first_nom":"Pig","last_nom":"Bodine"},
{"first_nom":"Lazslo","last_nom":"Jamf"},
{"first_nom":"Pirate","last_nom":"Prentice"}]"

Adapted based on this source: Code Snippet: How to Sort An Array of JSON Objects By Property

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Luke Schoen
  • 4,129
  • 2
  • 27
  • 25
5

It works for me. Here It will keep undefined to the end.

 function sort(items, property, direction) {

    function compare(a, b) {
      if(!a[property] && !b[property]) {
        return 0;
      } else if(a[property] && !b[property]) {
        return -1;
      } else if(!a[property] && b[property]) {
        return 1;
      } else {
        const value1 = a[property].toString().toUpperCase(); // ignore upper and lowercase
        const value2 = b[property].toString().toUpperCase(); // ignore upper and lowercase
        if (value1 < value2) {
          return direction === 0 ? -1 : 1;
        } else if (value1 > value2) {
          return direction === 0 ? 1 : -1;
        } else {
          return 0;
        }
        
      }
    }
    
    return items.sort(compare);
   } 
   
   var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: undefined, value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];
   console.log('Ascending Order:- ');
   console.log(sort(items, 'name', 0));
   console.log('Decending Order:- ');
   console.log(sort(items, 'name', 1));
    
    
chandan gupta
  • 1,185
  • 1
  • 10
  • 7
5

I've been using this utility in a variety of projects and it works great. It's very modular too:

  • Pass the name of the key to sort by
  • Choose if the sorting is ascending or descending

sortArrayOfObjsByKeyUtil.js

// Sort array of objects by key
// ------------------------------------------------------------
const sortArrayOfObjsByKey = (array, key, ascdesc) =>
  array.sort((a, b) => {
    const x = a[key];
    const y = b[key];
    if (ascdesc === 'asc') {
      return x < y ? -1 : x > y ? 1 : 0;
    }
    if (ascdesc === 'desc') {
      return x > y ? -1 : x < y ? 1 : 0;
    }
    return null;
  });

sortArrayOfObjsByKeyUtil.test.js

import sortArrayOfObjsByKey from './sortArrayOfObjsByKeyUtil';

const unsortedArray = [
  {
    _id: '3df55221-ce5c-4147-8e14-32effede6133',
    title: 'Netlife Design',
    address: {
      PostalAddress: {
        streetAddress: 'Youngstorget 3',
        addressLocality: 'Oslo',
        addressRegion: null,
        postalCode: '0181',
        addressCountry: 'Norway',
      },
    },
    geopoint: { lat: 59.914322, lng: 10.749272 },
  },
  {
    _id: 'cd00459f-3755-49f1-8847-66591ef935b2',
    title: 'Home',
    address: {
      PostalAddress: {
        streetAddress: 'Stockfleths gate 58A',
        addressLocality: 'Oslo',
        addressRegion: null,
        postalCode: '0461',
        addressCountry: 'Norway',
      },
    },
    geopoint: { lat: 59.937316, lng: 10.751862 },
  },
];

const sortedArray = [
  {
    _id: 'cd00459f-3755-49f1-8847-66591ef935b2',
    title: 'Home',
    address: {
      PostalAddress: {
        streetAddress: 'Stockfleths gate 58A',
        addressLocality: 'Oslo',
        addressRegion: null,
        postalCode: '0461',
        addressCountry: 'Norway',
      },
    },
    geopoint: { lat: 59.937316, lng: 10.751862 },
  },
  {
    _id: '3df55221-ce5c-4147-8e14-32effede6133',
    title: 'Netlife Design',
    address: {
      PostalAddress: {
        streetAddress: 'Youngstorget 3',
        addressLocality: 'Oslo',
        addressRegion: null,
        postalCode: '0181',
        addressCountry: 'Norway',
      },
    },
    geopoint: { lat: 59.914322, lng: 10.749272 },
  },
];

describe('sortArrayOfObjsByKey', () => {
  it(`sort array by 'title' key, ascending`, () => {
    const testInput = sortArrayOfObjsByKey(unsortedArray, 'title', 'asc');
    const testOutput = sortedArray;
    expect(testInput).toEqual(testOutput);
  });
});

Wallace Sidhrée
  • 11,221
  • 6
  • 47
  • 58
4

Using Lodash or Underscore.js, it’s a piece of cake:

const sortedList = _.orderBy(objs, [last_nom], [asc]); // Ascending or descending
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
karthik006
  • 886
  • 9
  • 19
3

I just enhanced Ege Özcan's dynamic sort to dive deep inside objects.

If the data looks like this:

obj = [
    {
        a: { a: 1, b: 2, c: 3 },
        b: { a: 4, b: 5, c: 6 }
    },
    {
        a: { a: 3, b: 2, c: 1 },
        b: { a: 6, b: 5, c: 4 }
}];

And if you want to sort it over a a.a property, I think my enhancement helps very well. I add new functionality to objects like this:

Object.defineProperty(Object.prototype, 'deepVal', {
    enumerable: false,
    writable: true,
    value: function (propertyChain) {
        var levels = propertyChain.split('.');
        parent = this;
        for (var i = 0; i < levels.length; i++) {
            if (!parent[levels[i]])
                return undefined;
            parent = parent[levels[i]];
        }
        return parent;
    }
});

And changed _dynamicSort's return function:

return function (a, b) {
    var result = ((a.deepVal(property) > b.deepVal(property)) - (a.deepVal(property) < b.deepVal(property)));
    return result * sortOrder;
}

And now you can sort by a.a. this way:

obj.sortBy('a.a');

See the complete script in a JSFiddle.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Morteza Tourani
  • 3,506
  • 5
  • 41
  • 48
3

It is also possible to make a dynamic sorting function when programming in TypeScript, but the types become more tricky in this case.

function sortByKey<O>(key: keyof O, decending: boolean = false): (a: O, b: O) => number {
    const order = decending ? -1 : 1;
    return (a, b): number => {
        const valA = a[key];
        const valB = b[key];
        if (valA < valB) {
            return -order;
        } else if (valA > valB) {
            return order;
        } else {
            return 0;
        }
    }
}

This can be used in TypeScript as the following:

const test = [
    {
        id: 0,
    },
    {
        id: 2,
    }
]

test.sort(sortByKey('id')) // OK
test.sort(sortByKey('id1')) // ERROR
test.sort(sortByKey('')) // ERROR
Ferrybig
  • 18,194
  • 6
  • 57
  • 79
3

This sorting function can be used for all object sorting:

  • object
  • deepObject
  • numeric array

You can also do ascending or descending sort by passing 1, -1 as the parameter.

Object.defineProperty(Object.prototype, 'deepVal', {
    enumerable: false,
    writable: true,
    value: function (propertyChain) {
        var levels = propertyChain.split('.');
        parent = this;
        for (var i = 0; i < levels.length; i++) {
            if (!parent[levels[i]])
                return undefined;
            parent = parent[levels[i]];
        }
        return parent;
    }
});

function dynamicSortAll(property, sortOrders=1) {

    /** The default sorting will be ascending order. If
        you need descending order sorting you have to
        pass -1 as the parameter **/

    var sortOrder = sortOrders;

    return function (a, b) {

        var result = (property? ((a.deepVal(property) > b.deepVal(property)) ? 1 : (a.deepVal(property) < b.deepVal(property)) ? -1 : 0) : ((a > b) ? 1 : (a < b) ? -1 : 0))

        return result * sortOrder;
    }
}

deepObj = [
    {
        a: { a: 1, b: 2, c: 3 },
        b: { a: 4, b: 5, c: 6 }
    },
    {
        a: { a: 3, b: 2, c: 1 },
        b: { a: 6, b: 5, c: 4 }
}];

let deepobjResult = deepObj.sort(dynamicSortAll('a.a', 1))
console.log('deepobjResult: ' + JSON.stringify(deepobjResult))
var obj = [
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];
let objResult = obj.sort(dynamicSortAll('last_nom', 1))
console.log('objResult: ' + JSON.stringify(objResult))

var numericObj = [1, 2, 3, 4, 5, 6]

let numResult = numericObj.sort(dynamicSortAll(null, -1))
console.log('numResult: ' + JSON.stringify(numResult))

let stringSortResult = 'helloworld'.split('').sort(dynamicSortAll(null, 1))

console.log('stringSortResult: ' + JSON.stringify(stringSortResult))

let uniqueStringOrger=[...new Set(stringSortResult)];
console.log('uniqueStringOrger: ' + JSON.stringify(uniqueStringOrger))
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jadli
  • 858
  • 1
  • 9
  • 17
3

Deep

Based on this excellent tutorial, I would like to develop Vlad Bezden's answer and explain why localeCompare is better than the standard comparison methods, like strA > strB. Let’s run this example:

console.log( 'Österreich' > 'Zealand' );  // We expect false
console.log( 'a' > 'Z' );                 // We expect false

The reason is that in JavaScript all strings are encoded using UTF-16 and

let str = '';

// Order of characters in JavaScript
for (let i = 65; i <= 220; i++) {
  str += String.fromCodePoint(i); // Code to character
}

console.log(str);

Capital letters go first (have small codes) and then go small letters and then go character Ö (after z). This is the reason why we get true in the first snippet - because operator > compares characters codes.

As you can see, compare characters in different languages is a nontrivial task - but luckily, modern browsers support the internationalization standard ECMA-402. So in JavaScript we have strA.localeCompare(strB) which does the job (-1 means strA is less than strB; 1 means the opposite; 0 means equal)

console.log( 'Österreich'.localeCompare('Zealand') ); // We expect -1
console.log( 'a'.localeCompare('Z') );                // We expect -1

I would like to add that localeCompare supports two parameters: language and additional rules:

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

objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom,'en',{sensitivity:'case'}))

console.log(objs);

// in '>' comparison 'Jamf' will NOT be next to 'jamf'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
2

I came into the problem of sorting array of objects, with changing the priority of values. Basically I want to sort an array of peoples by their age, and then by surname - or just by surname, name.

I think that this is simplest solution compared to other answers.

It’s is used by calling sortPeoples(['array', 'of', 'properties'], reverse=false).

/////////////////////// Example array of peoples ///////////////////////

var peoples = [
    {name: "Zach", surname: "Emergency", age: 1},
    {name: "Nancy", surname: "Nurse", age: 1},
    {name: "Ethel", surname: "Emergency", age: 1},
    {name: "Nina", surname: "Nurse", age: 42},
    {name: "Anthony", surname: "Emergency", age: 42},
    {name: "Nina", surname: "Nurse", age: 32},
    {name: "Ed", surname: "Emergency", age: 28},
    {name: "Peter", surname: "Physician", age: 58},
    {name: "Al", surname: "Emergency", age: 58},
    {name: "Ruth", surname: "Registration", age: 62},
    {name: "Ed", surname: "Emergency", age: 38},
    {name: "Tammy", surname: "Triage", age: 29},
    {name: "Alan", surname: "Emergency", age: 60},
    {name: "Nina", surname: "Nurse", age: 58}
];


//////////////////////// Sorting function /////////////////////
function sortPeoples(propertyArr, reverse) {
    function compare(a, b) {
        var i = 0;
        while (propertyArr[i]) {
            if (a[propertyArr[i]] < b[propertyArr[i]])
                return -1;
            if (a[propertyArr[i]] > b[propertyArr[i]])
                return 1;
            i++;
        }
        return 0;
    }
    peoples.sort(compare);

    if (reverse) {
        peoples.reverse();
    }
};

//////////////// End of sorting method ///////////////
function printPeoples() {
    $('#output').html('');
    peoples.forEach(function(person) {
        $('#output').append(person.surname + " " + person.name + " " + person.age + "<br>");
    })
}
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>

<html>

    <body>

        <button onclick="sortPeoples(['surname']); printPeoples()">sort by ONLY by surname ASC results in mess with same name cases</button><br>
        <button onclick="sortPeoples(['surname', 'name'], true); printPeoples()">sort by surname then name DESC</button><br>
        <button onclick="sortPeoples(['age']); printPeoples()">sort by AGE ASC. Same issue as in first case</button><br>
        <button onclick="sortPeoples(['age', 'surname']); printPeoples()">sort by AGE and Surname ASC. Adding second field fixed it.</button><br>

        <div id="output"></div>
    </body>

</html>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jmwierzbicki
  • 177
  • 9
2

This will sort a two-level nested array by the property passed to it in alphanumeric order.

function sortArrayObjectsByPropAlphaNum(property) {
    return function (a,b) {
        var reA = /[^a-zA-Z]/g;
        var reN = /[^0-9]/g;
        var aA = a[property].replace(reA, '');
        var bA = b[property].replace(reA, '');

        if(aA === bA) {
            var aN = parseInt(a[property].replace(reN, ''), 10);
            var bN = parseInt(b[property].replace(reN, ''), 10);
            return aN === bN ? 0 : aN > bN ? 1 : -1;
        } else {
            return a[property] > b[property] ? 1 : -1;
        }
    };
}

Usage:

objs.sort(utils.sortArrayObjectsByPropAlphaNum('last_nom'));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
depiction
  • 772
  • 6
  • 16
2

So here is one sorting algorithm which can sort in any order, throughout array of any kind of objects, without the restriction of datatype comparison (i.e., Number, String, etc.):

function smoothSort(items,prop,reverse) {
    var length = items.length;
    for (var i = (length - 1); i >= 0; i--) {
        //Number of passes
        for (var j = (length - i); j > 0; j--) {
            //Compare the adjacent positions
            if(reverse){
              if (items[j][prop] > items[j - 1][prop]) {
                //Swap the numbers
                var tmp = items[j];
                items[j] = items[j - 1];
                items[j - 1] = tmp;
            }
            }

            if(!reverse){
              if (items[j][prop] < items[j - 1][prop]) {
                  //Swap the numbers
                  var tmp = items[j];
                  items[j] = items[j - 1];
                  items[j - 1] = tmp;
              }
            }
        }
    }

    return items;
}
  • the first argument items is the array of objects,

  • prop is the key of the object on which you want to sort,

  • reverse is a Boolean parameter which on being true results in ascending order and in false it returns descending order.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Partha Roy
  • 1,575
  • 15
  • 16
2

Here’s a function you can use to sort the list by multiple objects, where if the first object is equal, the second order will be used as a fallback. Empty values should also be ignored to fallback order, if possible.

function sortObjects(list, orderBy){
    list.sort(function(a, b){
        let byIndex = 0;
        let order = orderBy[byIndex];
        while(!a[order.by] || !b[order.by] || a[order.by] === b[order.by]){
            byIndex++;
            if(byIndex >= orderBy.length){break;}
            order = orderBy[byIndex];
        }
        if(!a[order.by] || !b[order.by] || a[order.by] === b[order.by]){
            return false;
        }
        if(order.desc){
            return a[order.by] < b[order.by];
        }
        return a[order.by] > b[order.by];
    });
    return list;
}

Usage:

var objs = [
    {a: 10, b: 20, c: 30},
    {a: 30, b: 10, c: 20},
    {a: 20, b: 10, c: 30},
];

sortObjectList(objs, [{by: 'a'}]);
[
    {a: 10, b: 20, c: 30},
    {a: 20, b: 10, c: 30},
    {a: 30, b: 10, c: 20},
]

sortObjectList(objs, [{by: 'a', desc: true}]);
[
    {a: 30, b: 10, c: 20},
    {a: 20, b: 10, c: 30},
    {a: 10, b: 20, c: 30},
]

sortObjectList(objs, [{by: 'b', desc: true}, {by: 'c'}]);
[
    {a: 10, b: 20, c: 30},
    {a: 30, b: 10, c: 20},
    {a: 20, b: 10, c: 30},
]

Another example:

var objs = [
    {a: 5, b: 5},
    {a: 10, b: 15},
    {a: 15, b: 25},
    {b: 10},
    {b: 20},
    {a: 10, b: 30},
    {a: 10, b: 12},
];

sortObjectList(objs, [{by: 'a'}, {by: 'b'}]);
[
    {a: 5, b: 5},
    {b: 10},
    {a: 10, b: 12},
    {a: 10, b: 15},
    {b: 20},
    {a: 10, b: 30},
    {a: 15, b: 25},
]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SwiftNinjaPro
  • 117
  • 1
  • 7
2

For fp-holics:

const objectSorter = (p) => (a, b) => ((a, b) => a>b ? 1 : a<b ? -1 : 0)(a[p], b[p]);
objs.sort(objectSorter('first_nom'));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marinos An
  • 9,481
  • 6
  • 63
  • 96
1

I will give you a solution implementing a selection sort algorithm. It is simple and effective.

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


function selection_Sort(num) {
  //console.log(num);
  var temp, index;
  for (var i = 0; i <= num.length - 1; i++) {
    index = i;

    for (var j = i + 1; j <= num.length - 1; j++) {
      // You can use first_nom/last_nom, any way you choose to sort

      if (num[j].last_nom < num[index].last_nom) {
        index = j;
      }
    }

    // Below is the swapping part
    temp = num[i].last_nom;
    num[i].last_nom = num[index].last_nom;
    num[index].last_nom = temp;
  };
  console.log(num);
  return num;
}

selection_Sort(objs);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sg28
  • 1,363
  • 9
  • 19
1

In case you have nested objects

const objs = [{
        first_nom: 'Lazslo',
        last_nom: 'Jamf',
        moreDetails: {
            age: 20
        }
    }, {
        first_nom: 'Pig',
        last_nom: 'Bodine',
        moreDetails: {
            age: 21
        }
    }, {
        first_nom: 'Pirate',
        last_nom: 'Prentice',
        moreDetails: {
            age: 22
        }
    }];

nestedSort = (prop1, prop2 = null, direction = 'asc') => (e1, e2) => {
        const a = prop2 ? e1[prop1][prop2] : e1[prop1],
            b = prop2 ? e2[prop1][prop2] : e2[prop1],
            sortOrder = direction === "asc" ? 1 : -1
        return (a < b) ? -sortOrder : (a > b) ? sortOrder : 0;
    }

and call it like

objs.sort(nestedSort("last_nom"));
objs.sort(nestedSort("last_nom", null, "desc"));
objs.sort(nestedSort("moreDetails", "age"));
objs.sort(nestedSort("moreDetails", "age", "desc"));
Mas
  • 1,267
  • 1
  • 14
  • 13
1

Try this way:

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

const compareBylastNom = (a, b) => {
    // Converting to uppercase to have case-insensitive comparison
    const name1 = a.last_nom.toUpperCase();
    const name2 = b.last_nom.toUpperCase();

    let comparison = 0;

    if (name1 > name2) {
        comparison = 1;
    } else if (name1 < name2) {
        comparison = -1;
    }
    return comparison;
}

console.log(objs.sort(compareBylastNom));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Force Bolt
  • 1,117
  • 9
  • 9
1

I would do it like this:

[...objs].sort((a, b) => a.last_nom.localeCompare(b.last_nom))
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1
  var objs = [ 
    { firstName: 'A', lastName: 'Mark'  }, // b
    { firstName: 'E', lastName: 'askavy' }, // a
    { firstName: 'C', lastName: 'peter' }
];

objs.sort((a,b) => {
   return a.firstName.localeCompare(b.firstName) // Sort Ascending 
}) 

objs.sort((a,b) => {
   return b.firstName.localeCompare(a.firstName) // Sort Decending
}) 

 console.log(objs)
Avnish Jayaswal
  • 161
  • 1
  • 4
0

I know there are already plenty of answers, including those with localeCompare ones, but if you don't want to/can't use localeCompare for some reason, I would suggest you to use this solution instead of the ternary operator solution:

objects.sort((a, b) => (a.name > b.name) - (a.name < b.name));

Someone could say that it's not obvious what this code is doing, but in my opinion the ternary operator is worse. If one ternary operator is readable enough, two ternary operators one embedded into another — really hard to read and ugly. One-line code with just two comparison operators and one minus operator is very simple to read and thus to reason about.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rustam
  • 113
  • 1
  • 2
  • 9
0

I do like below.

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

objs.sort(function(a, b) {
  const nameA = a.name.toUpperCase(); // ignore upper and lowercase
  const nameB = b.name.toUpperCase(); // ignore upper and lowercase
  if (nameA < nameB) {
    return -1;
  }
  if (nameA > nameB) {
    return 1;
  }
  // names must be equal
  return 0;
});

console.log(arr);
Zia
  • 506
  • 3
  • 20
0

You can also add a generic function for sorting strings and numbers in ascending or descending order for example:

function sortArray({ data, key, sortingOrder }) {
    return data.sort((a, b) => {
      const firstValue = typeof a[key] === 'string' ? a[key]?.toLowerCase() : a[key];
      const secondValue = typeof b[key] === 'string' ? b[key]?.toLowerCase() : b[key];

      if (firstValue < secondValue) {
        return sortingOrder === SORT_ORDER.ASC ? -1 : 1;
      }
      if (firstValue > secondValue) {
        return sortingOrder === SORT_ORDER.ASC ? 1 : -1;
      }
      return 0;
    });
  }



const SORT_ORDER={
DES: 'Z-A',
ASC: 'A-Z'
};

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Zwalle'     },
    { first_nom: 'Pig',    last_nom: 'Podine'   },
    { first_nom: 'Pirate', last_nom: 'Antun' }
];

const sortedArray = sortArray({
data: objs,
key: 'last_nom',
sortingOrder: SORT_ORDER.ASC
});

console.log('sorted array', sortedArray);