Is it possible to sort a map using values in JS?
People can easily comprehend data when there is a pattern or order to it, and one of the easiest ones is the ascending, descending, or alphabetical order of data. It’s easier to monitor or use a series of numbers (e.g statistics) when they ascend or descend (1 to 10 or 10 to 1), or a series of words when they are alphabetical (a to Z or Z to a).
To achieve this necessary state, the sort
operation is important, and within JavaScript (JS), there are methods that help us to achieve this sort
operation. However, it can be a little complex when you have a key-value
pair relationship (such as objects, maps, etc.) between values and need to sort by the value
side.
In this article, we will understand how to sort a map by its value in JS in ascending or descending order when it comes to number and string values.
What are Maps?
The key-value pair structure
There are different data structures that programming languages like JS support, and are helpful when we design algorithms for our applications. Some data structures make use of key-value
relationships between different data types. Primitive data types such as String, Number, Boolean, and Symbol are often used as either the key
or value
in these types of data structures giving them a lot of extensive features. One of such data structures is the popular object
.
Objects
Objects allow us to group data values together to create relationships using the key-value
relationship. Often, objects keys
are called properties
and have what’s called a property-value
relationship. Objects, themselves, can hold other objects making for an interesting data structure.
Let’s create a simple object with the obj
binding.
let obj = {
name: "David",
age: 34,
relationship: "Single",
accountActivated: false
}
console.log(obj);
Output:
{
name: 'David',
age: 34,
relationship: 'Single',
accountActivated: false
}
The properties
we assign to the obj
object are not the only properties it holds and can often become an issue especially when we only need the properties we assign to it.
console.log("toString" in obj)
Output:
true
All the properties we assigned are simply enumerable, and the other properties are non-enumerable. The non-enumerable properties are obtained from the Object.prototype
which is used to create the object itself. The output above occurs because there is a toString
method that is inherited from Object.prototype
.
To have none of the properties from the Object.prototype
, we can make use of the data structure called Map
.
Maps
Maps are data structures that have key-value
relationships or associations where the keys and values can be of any data type (unlike objects where the key has to be a Symbol or String). With Maps, only the key and values we assign are present within it.
let map = new Map();
map.set("1-1", "foo");
map.set("0-1", "bar");
map.set("2-1", "baff");
console.log(map)
Output:
Map(3) { '1-1' => 'foo', '0-1' => 'bar', '2-1' => 'baff' }
The new Map()
creates the empty Map, and the set()
function insert new key-value
relationship. We can check the same toString
value within the map
binding we created.
console.log(map.has("toString"));
Output:
false
How to sort maps by key
To sort a map, we need a sort
function. Though the map
data structure doesn’t have its own, objects
(arrays or generally iterable) have a sort
function we can make use of. Let’s experiment with a simple object
let arr = [2, 3, 0, 9, 5, 2]
console.log(arr.sort());
Output:
[ 0, 2, 2, 3, 5, 9 ]
Now, we need a way to convert a Map to an iterable that we can sort, and there is a method for that, entries()
. The method returns an iterable of key, value pairs for every entry in the map.
let fruit = new Map();
fruit.set("orange", 12);
fruit.set("apple", 30);
fruit.set("banana", 20);
fruit.set("tangerine", 13);
console.log(fruit.entries())
Output:
[Map Entries] {
[ 'orange', 12 ],
[ 'apple', 30 ],
[ 'banana', 20 ],
[ 'tangerine', 13 ]
}
Now, we have an iterator object within contains arrays of the key
and value
at position 0
and 1
respectively for each array.
With this iterator object, we create a new array using the spread operator, ...
resulting in an array of arrays - [...fruit.entries()]
. On this array, we can apply the sort()
method. The sort()
method uses the first value of the inner array to sort the array of arrays.
console.log([...fruit.entries()].sort());
Output:
[
[ 'apple', 30 ],
[ 'banana', 20 ],
[ 'orange', 12 ],
[ 'tangerine', 13 ]
]
Using this sorted array, we can create a new Map using the new Map()
expression
let sortedFruit = new Map([...fruit.entries()].sort());
console.log(sortedFruit);
Output:
Map(4) {
'apple' => 30,
'banana' => 20,
'orange' => 12,
'tangerine' => 13
}
Different methods to sort maps by value
Now, let’s get to the crux of the matter. In the previous section, we sorted the map using just the key
value which is the first value (position 0
) within the array of the Map Entries
. Here, we will map sort by value in JS using two methods depending on the data type of the values we have.
All the previous logic and code apply but with some addition to the sort()
expression. Previously, we didn’t pass any argument to the sort function. To sort the value
part of the key-value
pair, we will need to pass an anonymous function. Depending on the data type of the value, we can use different functions
Method 1: Sort By String Values
For String values, we can use the localeCompare
, which determines whether two strings are equivalent in the current or specified locale. If the reference string is lexicographically greater than the compare string, localCompare
returns a positive number
, and if the opposite, it returns a negative number
. However, if both strings are equivalent, it returns 0
.
console.log("b".localeCompare("a"));
console.log("a".localeCompare("Z"));
Output:
1
-1
So, with this function, we can use the anonymous function to compare the values.
Remember, when we use the map.entries()
method, we get an iterator object with arrays representing each Map entry. There are two values within each array (e.g [ 'apple', 30 ]
) with the first value representing the key
, and the second value representing the value
. Therefore, the value
will have a positional value of 1
within the array.
Let’s create a Map with string values
.
let map = new Map();
map.set("1-1", "foo");
map.set("0-1", "bar");
map.set("2-1", "baff");
map.set("3-1", "bafx");
console.log(map);
Output:
Map(4) {
'1-1' => 'foo',
'0-1' => 'bar',
'2-1' => 'baff',
'3-1' => 'bafx'
}
Now, let’s sort using the logic we have described
let sortedMapValues = new Map(
[...map.entries()].sort((a, b) => String(a[1]).localeCompare(b[1]))
);
console.log(sortedMapValues);
Output:
Map(4) {
'2-1' => 'baff',
'3-1' => 'bafx',
'0-1' => 'bar',
'1-1' => 'foo'
}
Everything is sorted alphabetically from a - z
. To start from z - a
, we can switch the reference string
and compared string
.
let sortedMapValues = new Map(
[...map.entries()].sort((a, b) => String(b[1]).localeCompare(a[1]))
);
console.log(sortedMapValues);
Output:
Map(4) {
'1-1' => 'foo',
'0-1' => 'bar',
'3-1' => 'bafx',
'2-1' => 'baff'
}
Method 2: Sort By Numeric Values
To sort numeric values, the simple process of subtraction helps with sorting the number where we compare the first value to the previous value, and the result of the subtraction determines its position.
Let’s use the same fruit
Map.
let fruit = new Map();
fruit.set("orange", 12);
fruit.set("apple", 30);
fruit.set("banana", 20);
fruit.set("tangerine", 13);
console.log(fruit);
Output:
Map(4) {
'orange' => 12,
'apple' => 30,
'banana' => 20,
'tangerine' => 13
}
For ascending order, we will subtract the second value from the first value
let sortedFruit = new Map([...fruit.entries()].sort((a, b) => a[1] - b[1]));
console.log(sortedFruit);
Output:
Map(4) {
'orange' => 12,
'tangerine' => 13,
'banana' => 20,
'apple' => 30
}
For descending order, we will subtract the first value from the second value
let sortedFruit = new Map([...fruit.entries()].sort((a, b) => b[1] - a[1]));
console.log(sortedFruit);
Output:
Map(4) {
'apple' => 30,
'banana' => 20,
'tangerine' => 13,
'orange' => 12
}
Summary
To map sort by value requires us to make use of two methods, sort()
and entries()
, regardless of the data type values. For string values, the use of the localeCompare
method helps us to sort lexicographically, and the compareFn
anonymous function allows sorting numbers.
Map sort by values in JS requires iterator object and an understanding of how they operate, and how the sort()
function works intricately. The further readings are good starting points.
Further Reading
Array.prototype.sort() - JavaScript | MDN (mozilla.org)
Map.prototype.entries() - JavaScript | MDN (mozilla.org)