Sorting an Array By 1 Variable Then By Another

Tyler J Funk
4 min readJan 4, 2021

I was inspired to write about sorting an array by two different variables because of an issue I ran into while creating a project I was working on for Flatiron School before I graduated. I wanted to create a “Top Ten Leaderboard” which sorted the leaders by one variable first, then sorted them again by a different variable.

In this case I wanted to sort by the amount of time the user took to complete the game, and then sort by the amount of “misses” they made throughout their game session.

Let’s discuss what that might look like. For example, each game session might be an object that looked like this:

{
user: "tyler",
totalTime: 63,
totalMisses: 1
}

In order for there to be a “Top Ten” there would have to be 10 games played for us to rank. Here is the array we will be trying to sort:

let allGames = [
{
user: "tyler",
totalTime: 63,
totalMisses: 1
},
{
user: "kaitlyn",
totalTime: 34,
totalMisses: 2
},
{
user: "ethan",
totalTime: 47,
totalMisses: 4
},
{
user: "ryan",
totalTime: 36,
totalMisses: 4
},
{
user: "kate",
totalTime: 23,
totalMisses: 2
},
{
user: "maddie",
totalTime: 64,
totalMisses: 5
},
{
user: "mike",
totalTime: 65,
totalMisses: 0
},
{
user: "james",
totalTime: 45,
totalMisses: 1
},
{
user: "chris",
totalTime: 50,
totalMisses: 3
},
{
user: "steve",
totalTime: 28,
totalMisses: 4
}
]

Finally, here is the result that I would like to achieve via a sorting algorithm, I want the algorithm to sort the games by totalMisses first, then by totalTime:

[
{
user: "mike",
totalTime: 65,
totalMisses: 0
},
{
user: "james",
totalTime: 45,
totalMisses: 1
},
{
user: "tyler",
totalTime: 63,
totalMisses: 1
},
{
user: "kate",
totalTime: 23,
totalMisses: 2
},
{
user: "kaitlyn",
totalTime: 34,
totalMisses: 2
},
{
user: "chris",
totalTime: 50,
totalMisses: 3
},
{
user: "steve",
totalTime: 28,
totalMisses: 4
},
{
user: "ryan",
totalTime: 36,
totalMisses: 4
},
{
user: "ethan",
totalTime: 47,
totalMisses: 4
},
{
user: "maddie",
totalTime: 64,
totalMisses: 5
}
]

With this list, we have sorted by accuracy, then by speed! However, I quickly realized achieving this result by creating a sorting algorithm wasn’t as simple as it sounded!

Figuring out how to sort by totalTime wasn’t so bad, JavaScript has a sort method which comes in handy:

const sortByMisses = (gamesArray) => {
return gamesArray.sort((a, b) => {
return a.totalMisses - b.totalMisses;
})
}
let sortedArray = sortByMisses(allGames)console.log(sortedArray) // array sorted by misses

However, if I were to now sort this list by totalTime, it would reorder the entire list of games. So how do we rearrange the games by totalTime without undoing the sort we just got from sortByMisses?

Well, we could make each quantity of misses it’s own array, for example, if there were 3 different games that all had 2 totalMisses, those 3 games would be in their own sub-array. Following this, we can sort each of the sub-arrays by totalTime, then flatten the outer array back to it’s original format to get our final result.

Let’s make the function that creates our array of sub-arrays now:

const createSubArrays = (sortedGames) => {
let distinctValues = []

sortedGames.forEach(game => {
if( !(distinctValues.includes(game.totalMisses)) ){
distinctValues = [...distinctValues, game.totalMisses]
}
})

let groupedGames = distinctValues.map(value => {
return sortedGames.filter(game => {
return value === game.totalMisses
})
})

return groupedGames;
}
let matrix = createSubArrays(sortedArray);

What happened here? In a nutshell, this adds every different quantity of totalMisses to the distinctValues array. Then it takes each value and returns a filtered array of all the games which has that value equal to totalMisses. If we call console.log(createSubArrays(sortedArray)), it leaves us with a result in this format:

[
[{…}],
[{…}, {…}],
[{…}, {…}],
[{…}],
[{…}, {…}, {…}],
[{…}]
]

The last step now is to sort each of these sub-arrays individually before flattening the outer array. Let’s take a look at that now:

const sortEachSubArray = (groupedGames) => {
let finalArray = groupedGames.map(subArray => {
return subArray.sort((a, b) => {
return a.totalTime - b.totalTime
})
})
return finalArray
}
let topTen = sortEachSubArray(matrix).flat()console.log(topTen) // our desired result from earlier!

Let’s talk about this for a bit; each sub-array argument passed into the groupedGames parameter gets sorted by totalTime and mapped to a new array called finalArray. Then, I took the result of calling sortEachSubArray and flattened it back into the original array format in the topTen variable.

Just like that, we have a list of 10 objects which have been sorted by 2 different variables, placing a higher priority on the totalMisses, but then also sorting by totalTime. It took a little bit more work than originally expected, but with a few helper methods, we were able to make it happen!

Conclusion

I realize this post is essentially about solving an algorithm challenge, but this one in particular really helped me break down the barriers and think outside of the box. I feel like this particular challenge is good for junior devs like myself to understand, so I felt this would be a good solution to share and fully break down.

Thanks for reading and as always, happy hacking!

--

--

Tyler J Funk

Full Stack Web Developer && Creative Thinker && Flatiron School Grad && Continuous Learner