Is it possible to sort a ES6 map object?

后端 未结 11 1611
滥情空心
滥情空心 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:07

    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.

    0 讨论(0)
  • 2020-11-28 22:07

    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:

    • Some more examples here: https://javascript.info/map-set
    • You can copy-paste the below code to following link, and modify it for your specific use case: https://www.jdoodle.com/execute-nodejs-online/

    .

    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.

    0 讨论(0)
  • 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)))

    0 讨论(0)
  • 2020-11-28 22:10

    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>
                        )
    
    0 讨论(0)
  • 2020-11-28 22:12

    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];
        })
    )
    
    0 讨论(0)
  • 2020-11-28 22:13

    Unfortunately, not really implemented in ES6. You have this feature with OrderedMap.sort() from ImmutableJS or _.sortBy() from Lodash.

    0 讨论(0)
提交回复
热议问题