Is it possible to sort the entries of a es6 map object?
var map = new Map();
map.set(\'2-1\', foo);
map.set(\'0-1\', bar);
results in:
According MDN documentation:
A Map object iterates its elements in insertion order.
You could do it this way:
var map = new Map();
map.set('2-1', "foo");
map.set('0-1', "bar");
map.set('3-1', "baz");
var mapAsc = new Map([...map.entries()].sort());
console.log(mapAsc)
Using .sort()
, remember that the array is sorted according to each character's Unicode code point value, according to the string conversion of each element. So 2-1, 0-1, 3-1
will be sorted correctly.
2 hours spent to get into details.
Note that the answer for question is already given at https://stackoverflow.com/a/31159284/984471
However, the question has keys that are not usual ones,
A clear & general example with explanation, is below that provides some more clarity:
.
let m1 = new Map();
m1.set(6,1); // key 6 is number and type is preserved (can be strings too)
m1.set(10,1);
m1.set(100,1);
m1.set(1,1);
console.log(m1);
// "string" sorted (even if keys are numbers) - default behaviour
let m2 = new Map( [...m1].sort() );
// ...is destructuring into individual elements
// then [] will catch elements in an array
// then sort() sorts the array
// since Map can take array as parameter to its constructor, a new Map is created
console.log('m2', m2);
// number sorted
let m3 = new Map([...m1].sort((a, b) => {
if (a[0] > b[0]) return 1;
if (a[0] == b[0]) return 0;
if (a[0] < b[0]) return -1;
}));
console.log('m3', m3);
// Output
// Map { 6 => 1, 10 => 1, 100 => 1, 1 => 1 }
// m2 Map { 1 => 1, 10 => 1, 100 => 1, 6 => 1 }
// Note: 1,10,100,6 sorted as strings, default.
// Note: if the keys were string the sort behavior will be same as this
// m3 Map { 1 => 1, 6 => 1, 10 => 1, 100 => 1 }
// Note: 1,6,10,100 sorted as number, looks correct for number keys
Hope that helps.
new Map([...map].sort((a, b) =>
// Some sort function comparing keys with a[0] b[0] or values with a[1] b[1]
// Be sure to return -1 if lower and, if comparing values, return 0 if equal
))
For example, comparing value strings, which can be equal, we pass a sort function that accesses [1] and has an equals condition that returns 0:
new Map([...map].sort((a, b) => (a[1] > b[1] && 1) || (a[1] === b[1] ? 0 : -1)))
Comparing key strings, which can't be equal (identical string keys would overwrite each other), we can skip the equals condition. However, we should still explicitly return -1, because returning a lazy a[0] > b[0]
incorrectly gives false (treated as 0, i.e. equals) when a[0] < b[0]
:
new Map([...map].sort((a, b) => a[0] > b[0] ? 1 : -1))
The .entries()
in [...map.entries()]
(suggested in many answers) is redundant, probably adding an extra iteration of the map unless the JS engine optimises that away for you.
In the simple test case, you can do what the question asks for with:
new Map([...map].sort())
...which, if the keys are all strings, compares squashed and coerced comma-joined key-value strings like '2-1,foo'
and '0-1,[object Object]'
, returning a new Map with the new insertion order:
Note: if you see only {}
in SO's console output, look in your real browser console
const map = new Map([
['2-1', 'foo'],
['0-1', { bar: 'bar' }],
['3-5', () => 'fuz'],
['3-2', [ 'baz' ]]
])
console.log(new Map([...map].sort()))
HOWEVER, it's not a good practice to rely on coercion and stringification like this. You can get surprises like:
const map = new Map([
['2', '3,buh?'],
['2,1', 'foo'],
['0,1', { bar: 'bar' }],
['3,5', () => 'fuz'],
['3,2', [ 'baz' ]],
])
// Compares '2,3,buh?' with '2,1,foo'
// Therefore sorts ['2', '3,buh?'] ******AFTER****** ['2,1', 'foo']
console.log('Buh?', new Map([...map].sort()))
// Let's see exactly what each iteration is using as its comparator
for (const iteration of map) {
console.log(iteration.toString())
}
Bugs like this are really hard to debug - don't risk it!
If you want to sort on keys or values, it's best to access them explicitly with a[0]
and b[0]
in the sort function, like this. Note that we should return -1
and 1
for before and after, not false
or 0
as with raw a[0] > b[0]
because that is treated as equals:
const map = new Map([
['2,1', 'this is overwritten'],
['2,1', '0,1'],
['0,1', '2,1'],
['2,2', '3,5'],
['3,5', '2,1'],
['2', ',9,9']
])
// For keys, we don't need an equals case, because identical keys overwrite
const sortStringKeys = (a, b) => a[0] > b[0] ? 1 : -1
// For values, we do need an equals case
const sortStringValues = (a, b) => (a[1] > b[1] && 1) || (a[1] === b[1] ? 0 : -1)
console.log('By keys:', new Map([...map].sort(sortStringKeys)))
console.log('By values:', new Map([...map].sort(sortStringValues)))
Perhaps a more realistic example about not sorting a Map object but preparing the sorting up front before doing the Map. The syntax gets actually pretty compact if you do it like this. You can apply the sorting before the map function like this, with a sort function before map (Example from a React app I am working on using JSX syntax)
Mark that I here define a sorting function inside using an arrow function that returns -1 if it is smaller and 0 otherwise sorted on a property of the Javascript objects in the array I get from an API.
report.ProcedureCodes.sort((a, b) => a.NumericalOrder < b.NumericalOrder ? -1 : 0).map((item, i) =>
<TableRow key={i}>
<TableCell>{item.Code}</TableCell>
<TableCell>{item.Text}</TableCell>
{/* <TableCell>{item.NumericalOrder}</TableCell> */}
</TableRow>
)
Convert Map
to an array using Array.from, sort array, convert back to Map
, e.g.
new Map(
Array
.from(eventsByDate)
.sort((a, b) => {
// a[0], b[0] is the key of the map
return a[0] - b[0];
})
)
Unfortunately, not really implemented in ES6. You have this feature with OrderedMap.sort() from ImmutableJS or _.sortBy() from Lodash.