Чи можна сортувати об'єкт карти ES6?


98

Чи можна сортувати записи об'єкта карти es6?

var map = new Map();
map.set('2-1', foo);
map.set('0-1', bar);

призводить до:

map.entries = {
    0: {"2-1", foo },
    1: {"0-1", bar }
}

Чи можна сортувати записи на основі їх ключів?

map.entries = {
    0: {"0-1", bar },
    1: {"2-1", foo }
}

4
Карти за своєю суттю не впорядковані (за винятком повторного порядку вставки, який вимагає сортування та [повторного] додавання).
user2864740

1
Сортуйте записи, коли ви перебираєте карту (тобто перетворюєте їх на масив)
Halcyon

Відповіді:


141

Відповідно до документації MDN:

Об'єкт Map повторює свої елементи в порядку вставки.

Ви можете зробити це таким чином:

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)

Використовуючи .sort(), пам’ятайте, що масив сортується відповідно до значення кодової точки Unicode кожного символу, відповідно до перетворення рядків кожного елемента. Так 2-1, 0-1, 3-1буде відсортовано правильно.


7
Ви можете скоротити цю функцію (a, b) до: var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));за допомогою функції стрілки (лямбда)
Jimmy Chandra

2
Насправді це гостро лексично порівнюється 2-1,fooз 0-1,barі3-1,baz
Бергі


4
Ці ...точки мають велике значення в іншому випадку ви намагаєтеся сортувати MapIterator
muttonUp

2
Якщо ключі на карті - це цифри, ви отримаєте невірні результати з номерами, такими 1e-9як введення 100в сортувану карту. Код, що працює з цифрами:new Map([...map.entries()].sort((e1, e2) => e1[0] - e2[0]))
cdalxndr

60

Коротка відповідь

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

Наприклад, порівнюючи рядки значень, які можуть бути рівними, ми передаємо функцію сортування, яка звертається до [1] і має умову рівності, яка повертає 0:

 new Map([...map].sort((a, b) => (a[1] > b[1] && 1) || (a[1] === b[1] ? 0 : -1)))

Порівнюючи рядки ключів, які не можуть бути рівними (однакові ключі рядків перезаписують один одного), ми можемо пропустити умову дорівнює. Однак нам все одно слід явно повернути -1, оскільки повернення ледачого a[0] > b[0]неправильно дає false (обробляється як 0, тобто дорівнює), коли a[0] < b[0]:

 new Map([...map].sort((a, b) => a[0] > b[0] ? 1 : -1))

Детально на прикладах

Значення .entries()in [...map.entries()](пропонується у багатьох відповідях) є зайвим, ймовірно, додаючи додаткову ітерацію карти, якщо движок JS не оптимізує це для вас.

У простому тестовому випадку ви можете зробити те, що запитує запитання:

new Map([...map].sort())

... який, якщо всі ключі є рядками, порівнює стиснуті та примусові з’єднані комами рядки ключ-значення типу '2-1,foo'і '0-1,[object Object]', повертаючи нову карту з новим порядком вставки:

Примітка: якщо ви бачите лише {}вихідні дані консолі SO, загляньте у свою реальну консоль браузера

const map = new Map([
  ['2-1', 'foo'],
  ['0-1', { bar: 'bar' }],
  ['3-5', () => 'fuz'],
  ['3-2', [ 'baz' ]]
])

console.log(new Map([...map].sort()))

Втім , покладатися на примус та строгіфікацію, як це, не є доброю практикою. Ви можете отримати такі сюрпризи, як:

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

Такі помилки насправді важко налагодити - не ризикуйте!

Якщо ви хочете сортувати за ключами або значеннями, найкраще отримати явний доступ до них за допомогою a[0]та b[0]у функції сортування, наприклад. Зверніть увагу , що ми повинні повернутися -1і 1для до і після, а НЕ falseабо , 0як з сирим , a[0] > b[0]тому що трактуються як рівні:

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


11
Яка відповідь, ми повинні виправити механізми виявлення SO. Щось цього хорошого тут не слід втратити.
serraosays

30

Перетворити Mapна масив за допомогою Array.from, сортувати масив, перетворити назад Map, наприклад

new Map(
  Array
    .from(eventsByDate)
    .sort((a, b) => {
      // a[0], b[0] is the key of the map
      return a[0] - b[0];
    })
)

1
[...map.values()].sort()не працює для мене, але Array.from(map.values()).sort()зробив
Робу Juurlink

2
Це насправді мені допомогло, без Array.from Typescript видав мені помилку компіляції.
icSapper

7

Ідея полягає в тому, щоб витягти ключі вашої карти в масив. Сортувати цей масив. Потім перегляньте цей відсортований масив, отримайте пару значень із невідсортованої карти та помістіть їх на нову карту. Нова карта буде впорядкована. Код нижче - це його реалізація:

var unsortedMap = new Map();
unsortedMap.set('2-1', 'foo');
unsortedMap.set('0-1', 'bar');

// Initialize your keys array
var keys = [];
// Initialize your sorted maps object
var sortedMap = new Map();

// Put keys in Array
unsortedMap.forEach(function callback(value, key, map) {
    keys.push(key);
});

// Sort keys array and go through them to put in and put them in sorted map
keys.sort().map(function(key) {
    sortedMap.set(key, unsortedMap.get(key));
});

// View your sorted map
console.log(sortedMap);

2
Невідсортовані клавіші можна визначити просто за допомогою unsortedMap.keys(). Також keys.sort().map...повинно бути keys.sort().forEach....
Faintsignal

5

Ви можете перетворити на масив і викликати на ньому методи сортування масиву:

[...map].sort(/* etc */);

3
І що? Ви отримуєте масив, а не карту чи об’єкт
Green

5
і ви втрачаєте ключ
гальма


3

Один із способів - отримати масив записів, відсортувати його, а потім створити нову карту з відсортованим масивом:

let ar = [...myMap.entries()];
sortedArray = ar.sort();
sortedMap = new Map(sortedArray);

Але якщо ви не хочете створювати новий об’єкт, а працювати над тим самим, ви можете зробити щось подібне:

// Get an array of the keys and sort them
let keys = [...myMap.keys()];
sortedKeys = keys.sort();

sortedKeys.forEach((key)=>{
  // Delete the element and set it again at the end
  const value = this.get(key);
  this.delete(key);
  this.set(key,value);
})

1

Фрагмент нижче сортує дану карту за її ключами і знову відображає ключі до об’єктів ключ-значення. Я використовував функцію localeCompare, оскільки моєю картою була string-> string map map.

var hash = {'x': 'xx', 't': 'tt', 'y': 'yy'};
Object.keys(hash).sort((a, b) => a.localeCompare(b)).map(function (i) {
            var o = {};
            o[i] = hash[i];
            return o;
        });

результат: [{t:'tt'}, {x:'xx'}, {y: 'yy'}];


1

Наскільки я бачу, в даний час неможливо правильно відсортувати карту.

Інші рішення, де Карта перетворюється на масив і сортується таким чином, мають таку помилку:

var a = new Map([[1, 2], [3,4]])
console.log(a);    // a = Map(2) {1 => 2, 3 => 4}

var b = a;
console.log(b);    // b = Map(2) {1 => 2, 3 => 4}

a = new Map();     // this is when the sorting happens
console.log(a, b); // a = Map(0) {}     b = Map(2) {1 => 2, 3 => 4}

Сортування створює новий об’єкт, а всі інші вказівники на невідсортований об’єкт ламаються.


1

2 години, витрачені на детальніше.

Зверніть увагу, що відповідь на запитання вже дано на https://stackoverflow.com/a/31159284/984471

Однак у запитанні є ключі, які не є звичайними.
Нижче наведено чіткий і загальний приклад із поясненням, який надає додаткову ясність:

.

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

Сподіваюся, це допомагає.


0

Можливо, більш реалістичний приклад того, як не сортувати об’єкт Карти, а підготувати сортування перед тим, як робити Карту. Синтаксис стає насправді досить компактним, якщо ви робите це так. Ви можете застосувати сортування перед функцією map таким чином, із функцією сортування перед map (Приклад із програми React, над якою я працюю, використовуючи синтаксис JSX)

Позначте, що я тут визначаю функцію сортування всередині за допомогою функції стрілки, яка повертає -1, якщо вона менша, а 0 в іншому випадку сортується за властивістю об'єктів Javascript у масиві, який я отримую від 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>
                    )

-2
let map = new Map();
map.set('2-1', "foo");
map.set('0-1', "bar");
map.set('3-1', "baz");
let mapAsc = new Map([...map.entries()].sort());
console.log(mapAsc);

// Map(3) {"0-1" => "bar", "2-1" => "foo", "3-1" => "baz"}

3
Незважаючи на те, що це може відповісти на питання авторів, у ньому відсутні деякі пояснювальні слова та посилання на документацію. Фрагменти необробленого коду не дуже корисні без деяких фраз навколо нього. Будь ласка, відредагуйте свою відповідь.
hellow

2
Крім того, це 3-річне питання. Як ви думаєте, що пропонує ваша відповідь, чого інші відповіді ще не охоплювали?
hellow
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.