Можно ли отсортировать объект карты 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 01

1
Сортируйте записи, когда вы перебираете карту (то есть превращаете ее в массив)
Halcyon

Ответы:


140

Согласно документации 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]));используя стрелочную функцию (лямбда)
Джимми Чандра

2
Фактически, это фактически лексически сравнивается 2-1,fooс 0-1,barи3-1,baz
Берги


3
Эти ...точки имеют большое значение в противном случае вы пытаетесь сортировать MapIterator
muttonUp

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

59

Короткий ответ

 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()В [...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]в ней, например. Обратите внимание, что мы должны возвращать -1and 1for before и after, not falseили 0as с raw, 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. Что-то такое хорошее не должно быть потеряно здесь.
говорит serraos

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

1
Это действительно помогло мне, без 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
И что? Вы получаете массив, а не карту или объект
Грин

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, так как моя карта была строковой-> строковой объектной картой.

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
Кроме того, это вопрос трехлетней давности. Как вы думаете, что предлагает ваш ответ, чего другие ответы еще не охватили?
hellow
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.