Multi-level sorting in programming – Priority based sorting



This content originally appeared on DEV Community and was authored by Hamdan Khan

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"},
]

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"},
]
  • 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"},
]

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"},
]

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


This content originally appeared on DEV Community and was authored by Hamdan Khan