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)