Is it possible to sort a ES6 map object?

后端 未结 11 1727
滥情空心
滥情空心 2020-11-28 21:34

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:

11条回答
  •  臣服心动
    2020-11-28 22:10

    Short answer

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

    In detail with examples

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

提交回复
热议问题