É possível classificar as entradas de um objeto de mapa es6?
var map = new Map();
map.set('2-1', foo);
map.set('0-1', bar);
resulta em:
map.entries = {
0: {"2-1", foo },
1: {"0-1", bar }
}
É possível classificar as entradas com base em suas chaves?
map.entries = {
0: {"0-1", bar },
1: {"2-1", foo }
}
javascript
ecmascript-6
Ivan Bacher
fonte
fonte
Respostas:
De acordo com a documentação MDN:
Você poderia fazer desta forma:
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)
Usando
.sort()
, lembre-se de que a matriz é classificada de acordo com o valor do ponto de código Unicode de cada caractere, de acordo com a conversão da string de cada elemento. Então2-1, 0-1, 3-1
, será classificado corretamente.fonte
var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));
usando a função de seta (lambda)2-1,foo
a0-1,bar
e3-1,baz
(a,b) => a[0] > b[0]
!...
pontos são importantes, caso contrário, você está tentando classificar um MapIterator1e-9
foram colocados depois100
no mapa classificado. Código que funciona com números:new Map([...map.entries()].sort((e1, e2) => e1[0] - e2[0]))
Resposta curta
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 ))
Por exemplo, comparando strings de valor, que podem ser iguais, passamos uma função de classificação que acessa [1] e tem uma condição de igual que retorna 0:
new Map([...map].sort((a, b) => (a[1] > b[1] && 1) || (a[1] === b[1] ? 0 : -1)))
Comparando strings de chave, que não podem ser iguais (chaves de string idênticas se sobrescreveriam), podemos pular a condição de igual. No entanto, ainda devemos retornar explicitamente -1, porque retornar um preguiçoso
a[0] > b[0]
incorretamente resulta em falso (tratado como 0, ou seja, igual) quandoa[0] < b[0]
:new Map([...map].sort((a, b) => a[0] > b[0] ? 1 : -1))
Em detalhes com exemplos
O
.entries()
in[...map.entries()]
(sugerido em muitas respostas) é redundante, provavelmente adicionando uma iteração extra do mapa, a menos que o mecanismo JS otimize isso para você.No caso de teste simples, você pode fazer o que a pergunta pede com:
new Map([...map].sort())
... que, se as chaves forem todas strings, compara strings de valores-chave unidos por vírgulas e comprimidas como
'2-1,foo'
e'0-1,[object Object]'
, retornando um novo Mapa com o novo pedido de inserção:Nota: se você vir apenas
{}
na saída do console do SO, olhe no console do seu navegador realconst map = new Map([ ['2-1', 'foo'], ['0-1', { bar: 'bar' }], ['3-5', () => 'fuz'], ['3-2', [ 'baz' ]] ]) console.log(new Map([...map].sort()))
NO ENTANTO , não é uma boa prática confiar em coerção e estringificação como esta. Você pode obter surpresas como:
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 como esse são realmente difíceis de depurar - não se arrisque!
Se você deseja classificar as chaves ou valores, é melhor acessá-los explicitamente com
a[0]
eb[0]
na função de classificação, como este. Observe que devemos retornar-1
e1
para antes e depois, nãofalse
ou0
como rawa[0] > b[0]
porque isso é tratado como igual: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)))
fonte
Converter
Map
para um array usandoArray.from
, classificar array, converter de volta paraMap
, por exemplonew Map( Array .from(eventsByDate) .sort((a, b) => { // a[0], b[0] is the key of the map return a[0] - b[0]; }) )
fonte
[...map.values()].sort()
não funcionou para mim, masArray.from(map.values()).sort()
funcionouA ideia é extrair as chaves do seu mapa em um array. Classifique esta matriz. Em seguida, itere sobre essa matriz classificada, obtenha seu par de valores do mapa não classificado e coloque-os em um novo mapa. O novo mapa estará em ordem. O código abaixo é sua implementação:
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);
fonte
unsortedMap.keys()
. Tambémkeys.sort().map...
deveria serkeys.sort().forEach...
.Você pode converter em uma matriz e chamar métodos de soring de matriz nela:
[...map].sort(/* etc */);
fonte
Infelizmente, não é realmente implementado no ES6. Você tem esse recurso com OrderedMap.sort () de ImmutableJS ou _.sortBy () de Lodash.
fonte
Uma maneira é obter a matriz de entradas, classificá-la e, em seguida, criar um novo Mapa com a matriz classificada:
let ar = [...myMap.entries()]; sortedArray = ar.sort(); sortedMap = new Map(sortedArray);
Mas se você não quiser criar um novo objeto, mas trabalhar no mesmo, você pode fazer algo assim:
// 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); })
fonte
O fragmento abaixo classifica o mapa fornecido por suas chaves e mapeia as chaves para objetos de valor-chave novamente. Usei a função localeCompare, pois meu mapa era string-> string object 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; });
resultado:
[{t:'tt'}, {x:'xx'}, {y: 'yy'}];
fonte
Pelo que vejo, atualmente não é possível classificar um mapa corretamente.
As outras soluções em que o Mapa é convertido em uma matriz e classificado dessa forma apresentam o seguinte bug:
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}
A classificação cria um novo objeto e todos os outros ponteiros para o objeto não classificado são quebrados.
fonte
2 horas gastas para entrar em detalhes.
Observe que a resposta para a pergunta já foi fornecida em https://stackoverflow.com/a/31159284/984471
No entanto, a pergunta tem chaves que não são usuais.
Um exemplo claro e geral com explicação está abaixo, que fornece mais clareza:
.
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
Espero que ajude.
fonte
Talvez um exemplo mais realista sobre não classificar um objeto de mapa, mas preparar a classificação antes de fazer o mapa. A sintaxe fica realmente muito compacta se você fizer assim. Você pode aplicar a classificação antes da função de mapa desta forma, com uma função de classificação antes do mapa (exemplo de um aplicativo React no qual estou trabalhando usando a sintaxe JSX)
Marque que eu aqui defino uma função de classificação interna usando uma função de seta que retorna -1 se for menor e 0, caso contrário, classificada em uma propriedade dos objetos Javascript na matriz que obtenho de uma 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> )
fonte
Ligeira variação - eu não espalhei a sintaxe e queria trabalhar em um em
object
vez de em umMap
.Object.fromEntries(Object.entries(apis).sort())
fonte