DEV Community

Hamdan Khan
Hamdan Khan

Posted on

Multi-level sorting in programming - Priority based sorting

Have you ever stumbled accross the need for sorting your data not on the basis of a single but multiple attributes?

Well let's go through an example:
Suppose we have an array of data: scores, which holds the records of let's say results of people who took part in a competiton. The requirement is to prepare the Final Rankings of the participants i.e. sort the data by scores in descending order.

A simpler un-sorted version of the data, for the sake of explanation, would look somewhat like this:

scores = [
    {score: 90, updatedAt: "2024-06-01", name: "John"},
    {score: 90, updatedAt: "2024-06-01", name: "Bob"},
    {score: 80, updatedAt: "2024-06-02", name: "Charlie"},
    {score: 90, updatedAt: "2024-05-30", name: "Dave"},
    {score: 80, updatedAt: "2024-05-30", name: "Eve"},
]
Enter fullscreen mode Exit fullscreen mode

Sorting this data seems pretty easy. Array.prototype.sort() can be used to sort the array in descending order of scores.

scores.sort((a,b) => b.score - a.score);

// after sorting
[
    {score: 90, updatedAt: "2024-06-01", name: "John"},
    {score: 90, updatedAt: "2024-06-01", name: "Bob"},
    {score: 90, updatedAt: "2024-05-30", name: "Dave"},
    {score: 80, updatedAt: "2024-06-02", name: "Charlie"},
    {score: 80, updatedAt: "2024-05-30", name: "Eve"},
]
Enter fullscreen mode Exit fullscreen mode
  • John, bob and Dave took the joint first place.
  • Charlie and Eve tied for the second place.

Multi-level sorting

If we take a deeper look at the scores data, we see that people participated in the competition at different dates. For example, Bob participated on 2024-06-01 whereas Dave participated on 2024-05-30.
For participants with the same score, isn't that a bit unfair to those who participated earlier to be placed below the participants who participated later?

So how do we sort the array now, such that:

  • The data is sorted in descending order of scores.
  • For records with same scores, data is sorted in the ascending order of participation dates (earlier -> later).

Would sorting the data twice based on the attributes get us our desired result?

scores.sort((a,b) => b.score - a.score);
scores.sort((a,b) => a.updatedAt - b.updatedAt);

// after sorting 
[    
    {score: 90, updatedAt: "2024-05-30", name: "Dave"},
    {score: 80, updatedAt: "2024-05-30", name: "Eve"},
    {score: 90, updatedAt: "2024-06-01", name: "John"},
    {score: 90, updatedAt: "2024-06-01", name: "Bob"},
    {score: 80, updatedAt: "2024-06-02", name: "Charlie"},
]
Enter fullscreen mode Exit fullscreen mode

Nope, the data would be sorted to the latest sorting order instead i.e. ascending order of participation dates. This looks nothing like a Final Ranking.

Solution

The solution is to sort the data based on multiple conditions. Or we can say sort it on multiple levels:

scores.sort((a,b) => {
    // if scores are not same, sort based on the desc order of score
    if (a.score !== b.score) {
        return b.score - a.score;
    }
    // otherwise sort based on the asc order of participation date
    return a.updatedAt - b.updatedAt;
});

// after sorting
[    
    {score: 90, updatedAt: "2024-05-30", name: "Dave"},
    {score: 90, updatedAt: "2024-06-01", name: "John"},
    {score: 90, updatedAt: "2024-06-01", name: "Bob"},
    {score: 80, updatedAt: "2024-05-30", name: "Eve"},
    {score: 80, updatedAt: "2024-06-02", name: "Charlie"},
]
Enter fullscreen mode Exit fullscreen mode

Now this looks more like a fair Final Ranking.

The sorting conditions can be simplified further using the OR operator:

scores.sort((a,b) => (b.score - a.score) || (a.updatedAt - b.updatedAt));
Enter fullscreen mode Exit fullscreen mode

Top comments (0)