134
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);      
    return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});

This code sorts the array by gsize, smallest to largest.

How would I change it to sort first by gsize and then by glow?

3
  • 1
    sort function reacts on positive, negative or zero result. so you can just write: "return aSize - bSize". it will be more simple and readable code. Commented May 1, 2014 at 14:50
  • Top answer, compressed: Sort array of objects by col0 then col1 (ascending): myArray.sort(function(a,b){return a.col0-b.col0||a.col1-b.col1}); Alternate example: Sort array of arrays by index #0, then #2, then #1 (descending): myArray.sort(function(a,b){return b[0]-a[0]||b[2]-a[2]||b[1]-a[1]}); Commented Dec 16, 2021 at 16:57
  • Covered by the more general "How to sort an array of objects by multiple fields?" Commented Feb 3, 2022 at 9:05

19 Answers 19

246
grouperArray.sort(function (a, b) {   
    return a.gsize - b.gsize || a.glow - b.glow;
});

shorter version

Sign up to request clarification or add additional context in comments.

6 Comments

great short cut! helped me put a more complex solution together.. stackoverflow.com/questions/6101475/…
Nice and clean! The only thing it only works for numbers.
Can you explain the logic in here ?!. It worked for me to sort an array with a key's value first and then sort the result with another key's value
@KTM The logic goes as follow: if both gsize are equal, then the first part of the condition is equal to 0, which is considered as false, and the second part of the condition is executed.
@Scalpweb Yeah :) so this works to sort an array with any number of keys one by one right?! Nice trick
|
132
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);

    if(aSize == bSize)
    {
        return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
    }
    else
    {
        return (aSize < bSize) ? -1 : 1;
    }
});

1 Comment

To make this more concise using arrow syntax, without resorting to non-obvious techniques shown in some other answers: grouperArray.sort((a, b) => a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize. To make the code easier to understand, we keep explicit the test for ==.
58
grouperArray.sort((a, b) => a.gsize - b.gsize || a.glow - b.glow);

Even shorter version using arrow syntax!

2 Comments

Even shorter, take out the spaces: grouperArray.sort((a,b)=>a.gsize-b.gsize||a.glow-b.glow);
if you want to understand why that works you might wanna look at medium.com/@safareli/pss-ordering-is-a-monoid-61a4029387e
16

I realize this was asked some time ago, but I thought I would add my solution.

This function generates sort methods dynamically. simply supply each sortable child property name, prepended with +/- to indicate ascending or descending order. Super re-usable, and it doesn't need to know anything about the data structure you've put together. Could be made idiot proof - but doesn't seem necessary.

function getSortMethod(){
    var _args = Array.prototype.slice.call(arguments);
    return function(a, b){
        for(var x in _args){
            var ax = a[_args[x].substring(1)];
            var bx = b[_args[x].substring(1)];
            var cx;

            ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
            bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;

            if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
            if(ax != bx){return ax < bx ? -1 : 1;}
        }
    }
}

example usage:

items.sort(getSortMethod('-price', '+priority', '+name'));

this would sort items with lowest price first, with ties going to the item with the highest priority. further ties are broken by the item name

where items is an array like:

var items = [
    { name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
    { name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
    { name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
    { name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
    { name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];

live demo: http://gregtaff.com/misc/multi_field_sort/

EDIT: Fixed issue with Chrome.

1 Comment

for typescript (for not getting an error TS2554: Expected 0 arguments, but got ..) use the syntax here: stackoverflow.com/a/4116634/5287221
7

I expect the ternary operator ((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;) has you confused. You should check out the link to understand it better.

Until then, here's your code blown out into full if/else.

grouperArray.sort(function (a, b) {
    if (a.gsize < b.gsize)
    {
        return -1;
    }
    else if (a.gsize > b.gsize)
    {
        return 1;
    }
    else
    {
        if (a.glow < b.glow)
        {
            return -1;
        }
        else if (a.glow > b.glow)
        {
            return 1;
        }
        return 0;
    }
});

Comments

6

Here's an implementation for those who may want something more generic that would work with any number of fields.

Array.prototype.sortBy = function (propertyName, sortDirection) {

    var sortArguments = arguments;
    this.sort(function (objA, objB) {

        var result = 0;
        for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {

            var propertyName = sortArguments[argIndex];
            result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;

            //Reverse if sort order is false (DESC)
            result *= !sortArguments[argIndex + 1] ? 1 : -1;
        }
        return result;
    });

}

Basically, you may specify any number of property name / sort direction:

var arr = [{
  LastName: "Doe",
  FirstName: "John",
  Age: 28
}, {
  LastName: "Doe",
  FirstName: "Jane",
  Age: 28
}, {
  LastName: "Foo",
  FirstName: "John",
  Age: 30
}];

arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo

arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe

Comments

3
grouperArray.sort(function (a, b) {
  var aSize = a.gsize;
  var bSize = b.gsize;
  var aLow = a.glow;
  var bLow = b.glow;
  console.log(aLow + " | " + bLow);      
  return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});

Comments

3
grouperArray.sort(function (a, b) {
     var aSize = a.gsize;     
     var bSize = b.gsize;     
     var aLow = a.glow;
     var bLow = b.glow;
     console.log(aLow + " | " + bLow);
     return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); }); 

Comments

3

Here is an implementation that uses recursion to sort by any number of sort fields from 1 to infinite. You pass it a results array which is an array of result objects to sort, and a sorts array which is an array of sort objects defining the sort. Each sort object must have a "select" key for the key name that it sorts by and an "order" key which is a string indicating "ascending" or "descending".

sortMultiCompare = (a, b, sorts) => {
    let select = sorts[0].select
    let order = sorts[0].order
    if (a[select] < b[select]) {
        return order == 'ascending' ? -1 : 1
    } 
    if (a[select] > b[select]) {
        return order == 'ascending' ? 1 : -1
    }
    if(sorts.length > 1) {
        let remainingSorts = sorts.slice(1)
        return this.sortMultiCompare(a, b, remainingSorts)
    }
    return 0
}

sortResults = (results, sorts) => {
    return results.sort((a, b) => {
        return this.sortMultiCompare(a, b, sorts)
    })
}

// example inputs
const results = [
    {
        "LastName": "Doe",
        "FirstName": "John",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Doe",
        "FirstName": "Jane",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Johnson",
        "FirstName": "Kevin",
        "MiddleName": "Bill"
    }
]

const sorts = [
    {
        "select": "LastName",
        "order": "ascending"
    },
    {
        "select": "FirstName",
        "order": "ascending"
    },
    {
        "select": "MiddleName",
        "order": "ascending"
    }    
]

// call the function like this:
let sortedResults = sortResults(results, sorts)

Comments

3

A dynamic way to do that with MULTIPLE keys:

  • filter unique values from each col/key of sort
  • put in order or reverse it
  • add weights width zeropad for each object based on indexOf(value) keys values
  • sort using caclutated weights

enter image description here

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );

        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });

    return this;
}
});

Use:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

enter image description here

Comments

1

This is what I use

function sort(a, b) {
    var _a = "".concat(a.size, a.glow);
    var _b = "".concat(b.size, b.glow);
    return _a < _b;
}

concat the two items as a string and they will be sorted by a string value. If you want you could wrap _a and _b with parseInt to compare them as numbers if you know they will be numerical.

Comments

1

Here is the solution for the case, when you have a priority sort key, which might not exist in some particular items, so you have to sort by fallback keys.

An input data example (id2 is priority sort key):

const arr = [
    {id: 1},
    {id: 2, id2: 3},
    {id: 4},
    {id: 3},
    {id: 10, id2: 2},
    {id: 7},
    {id: 6, id2: 1},
    {id: 5},
    {id: 9, id2: 2},
    {id: 8},
];

And the output should be:

[ { id: 6, id2: 1 },
  { id: 9, id2: 2 },
  { id: 10, id2: 2 },
  { id: 2, id2: 3 },
  { id: 1 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 7 },
  { id: 8 } ]

The comparator function will be like:

arr.sort((a,b) => {
  if(a.id2 || b.id2) {
    if(a.id2 && b.id2) {
      if(a.id2 === b.id2) {
        return a.id - b.id;
      }
      return a.id2 - b.id2;
    }
    return a.id2 ? -1 : 1;
  }
  return a.id - b.id
});

P.S. In case if .id of .id2 can be zeros, consider to use typeof.

Comments

1

Let's simplify.

Say you have an array of arrays:

let tmp = [
    [0, 1],
    [2, 1],
    [1, 1],
    [0, 0],
    [2, 0],
    [1, 0],
    [0, 2],
    [2, 2],
    [1, 2],
]

Executing:

tmp.sort((a, b) => {
    if (a[1] != b[1])
        return a[1] - b[1];
    else
        return a[0] - b[0];
})

Will yield:

[
    [0, 0],
    [1, 0],
    [2, 0],
    [0, 1],
    [1, 1],
    [2, 1],
    [0, 2],
    [1, 2],
    [2, 2]
]

Comments

1
var items = [
    { name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
    { name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
    { name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
    { name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
    { name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 }];

items.sort(function (a, b) {
    var nameA = a.name.toUpperCase(); 
    var nameB = b.name.toUpperCase();   
    var nameC = a.price.toUpperCase(); 
    var nameD = b.price.toUpperCase(); 

    if (nameA < nameB) {
            return -1;
        }
        if (nameA > nameB || nameC > nameD) {
            return 1;
        }

        // names must be equal
        return 0;
    });`

1 Comment

Hi @aafridah . Welcome to StackOverflow. Please consider adding a little bit of description while submitting a code snippet. Try to describe what your code does and how exactly will it solve the problem.
0
grouperArray.sort(
  function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);

Comments

0
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    if (aSize !== aSize)
        return aSize - bSize;
    return a.glow - b.glow;
});

not tested, but I think that should work.

Comments

0

In my case, i sort notification list by param 'important' and by 'date'

  • step 1: i filter notifications by 'important' and unImportant

    let importantNotifications = notifications.filter(
            (notification) => notification.isImportant);
    
      let unImportantNotifications = notifications.filter(
            (notification) => !notification.isImportant);
    
  • step 2: i sort them by date

      sortByDate = (notifications) => {
      return notifications.sort((notificationOne, notificationTwo) => {
        return notificationOne.date - notificationTwo.date;
      });
    };
    
  • step 3: merge them

    [
        ...this.sortByDate(importantNotifications),
        ...this.sortByDate(unImportantNotifications),
      ];
    

Comments

0

If you're happy to use the new tidy.js package you can achieve this with

tidy(input_array,
  arrange(['var1', desc('var2')])
);

Comments

0

Besides the other answers here I got inconsistent data on my arrays where 1 wanted a primary ASC sort on field x and a secondary DESC sort on field y.
The solution is in giving the primary sort more importance by multiplying the number with lets say 1000000000

arrayOfObjects.sort((a, b) => {
    return (
        // Multiply by a high number to the most important sort, that makes them heavier than the second sort

        // First sort ASC (notice the - minus in the end instead of the || in other answers !)
        (a.paramX * 1000000000) -
        (b.paramX * 1000000000) -

        // Second sort DESC (switch them if you want ASC too)
        (a.paramY - b.paramY)
    )
})

for sorting on multiple dates on the object it is this:

// param date1 ASC and param date2 DESC
arrayOfObjects.sort((a, b) => {
    return (
        (a.date1.getTime() * 1000000000) -
        (b.date1.getTime() * 1000000000) -
        (a.date2.getTime() - b.date2.getTime())
    )
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.