Istvan
Istvan

Reputation: 351

Sorting array based on multiple values of the same attribute in Javascript

Is there a more efficient way to sort an array based on multiple values of the same attribute in Javascript? I have the following function:

    var p1 = [];
    var p2 = [];
    var p3 = [];
    for (var i = 0; i < contentData.length; i++) {
        if (contentData[i].priority === 1) {
            p1.push(contentData[i]);
        }

        else if (contentData[i].priority === 2) {
            p2.push(contentData[i]);
        }

        else if (contentData[i].priority === 3) {
            p3.push(contentData[i]);
        }
    }
    p1.sort(sortByDateDesc);
    p2.sort(sortByDateDesc);
    p3.sort(sortByDateDesc);
    contentData = p1;
    Array.prototype.push.apply(contentData, p2);
    Array.prototype.push.apply(contentData, p3);

First I need to sort the array by its priority attribute, and then by its date attribute, which is done in the function sortByDateDesc. Can this be done in a more efficient way? Thanks!

Sample array:

var data1 = {"title": "His face looks like the best chair", "text": "So there’s this really hot kid in my creative writing class. And everyone knows I like him." +
"But one day, he walked in looking like a freaking GQ model, and I accidentally out loud whispered “Shit, his face looks like the best chair” and the girl who sits " +
"in front of me turned around and said “WTH, that’s freaky and gross” and she moved her seat." +
"She gives me weird looks every time she sees me now.", "url": "http://www.catfacts.co", "user": "Kash Muni", "timestamp": Date.now(), "read":0, "priority":2};

sortByDateDesc function:

function sortByDateDesc(a, b) {
    if (a.timestamp > b.timestamp)
        return -1;
    if (b.timestamp > a.timestamp)
        return 1;
    return 0;
}

Upvotes: 1

Views: 1218

Answers (2)

Raith
Raith

Reputation: 528

You are on the right track with your sortByDate function, which is passed in to the .sort() method as the "CompareFunction".

The CompareFunction that you provide will be passed two parameters, which are by-convention called "a" and "b", and must provide an answer which denotes their relative position:

  • If 'a comes before b' or if 'a is less than b' then return an answer < 0.
  • If 'a comes after b' or if 'a is greater than b' then return an answer > 0.
  • If 'a and b are equal' then you can return the answer 0.

It is entirely up to you how the CompareFunction deals with those objects. You might only compare a single property, compare multiple properties, or even perform complex processing within your function (but don't).

For comparisons which are numeric in nature, you can arithmetically determine the answer. E.g.

function sortNumbersInAscendingOrder(a, b) {
  return a - b;
}

If a > b then a - b results in a positive answer, which states that 'a comes after b'.

Comparing strings is a bit different as the comparison operators return a boolean value. However, you don't actually need to provide all three variants of answer from your CompareFunction - you are allowed to favour one of your objects and say that it comes first even when they're the same. This allows you to sort strings simply using the ternary operator. E.g.

function sortStringsAlphabeticalOrder(a, b) {
  // Note: this is a case sensitive sort!
  return (a <= b) ? -1 : 1;
}

As usual, the Mozilla docs provide an excellent technical answer: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Upvotes: 1

trincot
trincot

Reputation: 350127

You can use the || in the custom callback function. Something like this:

contentData.sort( (a, b) => a.priority - b.priority || b.timestamp - a.timestamp );

Only when a.priority - b.priority is zero (they are equal) will the second part of the expression be evaluated, which exactly when you want the date to play a role.

Swap a.timestamp and b.timestamp if the date order has to be ascending instead.

Upvotes: 6

Related Questions