Apakah mungkin untuk mengurutkan objek peta ES6?


98

Apakah mungkin untuk mengurutkan entri dari objek peta es6?

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

menghasilkan:

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

Apakah mungkin untuk mengurutkan entri berdasarkan kuncinya?

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

4
Peta secara inheren tidak diurutkan (kecuali urutan penyisipan yang berulang, yang memerlukan penyortiran dan kemudian [kembali] ditambahkan).
pengguna2864740

1
Urutkan entri saat Anda mengulang di atas peta (yaitu mengubahnya menjadi array)
Halcyon

Jawaban:


140

Menurut dokumentasi MDN:

Objek Map mengulang elemennya dalam urutan penyisipan.

Anda bisa melakukannya dengan cara ini:

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)

Menggunakan .sort(), ingatlah bahwa larik diurutkan sesuai dengan nilai poin kode Unicode setiap karakter, sesuai dengan konversi string dari setiap elemen. Jadi 2-1, 0-1, 3-1akan diurutkan dengan benar.


7
Anda dapat mempersingkat fungsi itu (a, b) menjadi: var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));menggunakan fungsi panah (lambda)
Jimmy Chandra

2
Faktanya, secara leksikal membandingkan 2-1,foodengan 0-1,bardan3-1,baz
Bergi


4
The ...titik yang penting jika Anda mencoba untuk mengurutkan MapIterator
muttonUp

2
Jika kunci peta adalah angka, Anda akan mendapatkan hasil yang tidak valid dengan angka seperti 1e-9diletakkan setelah 100di peta yang diurutkan. Kode yang berfungsi dengan angka:new Map([...map.entries()].sort((e1, e2) => e1[0] - e2[0]))
cdalxndr

60

Jawaban singkat

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

Misalnya, membandingkan string nilai, yang bisa sama, kita meneruskan fungsi sortir yang mengakses [1] dan memiliki kondisi sama dengan yang mengembalikan 0:

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

Membandingkan string kunci, yang tidak bisa sama (kunci string identik akan menimpa satu sama lain), kita dapat melewati kondisi sama dengan. Namun, kita tetap harus secara eksplisit mengembalikan -1, karena mengembalikan lazy a[0] > b[0]salah memberikan false (diperlakukan sebagai 0, yaitu sama) ketika a[0] < b[0]:

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

Secara detail dengan contoh

The .entries()dalam [...map.entries()](disarankan dalam banyak jawaban) berlebihan, mungkin menambahkan iterasi ekstra peta kecuali mengoptimalkan mesin JS yang pergi untuk Anda.

Dalam kasus pengujian sederhana, Anda dapat melakukan apa yang diminta pertanyaan dengan:

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

... yang, jika semua kuncinya adalah string, membandingkan string nilai kunci yang digabung dan dipaksakan seperti '2-1,foo'dan '0-1,[object Object]', menampilkan Peta baru dengan urutan penyisipan baru:

Catatan: jika Anda hanya melihat {}di keluaran konsol SO, lihat di konsol browser Anda yang sebenarnya

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

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

NAMUN , bukanlah praktik yang baik untuk mengandalkan paksaan dan stringifikasi seperti ini. Anda bisa mendapatkan kejutan seperti:

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

Bug seperti ini sangat sulit untuk di-debug - jangan ambil risiko!

Jika Anda ingin mengurutkan kunci atau nilai, yang terbaik adalah mengaksesnya secara eksplisit dengan a[0]dan b[0]dalam fungsi sortir, seperti ini. Perhatikan bahwa kita harus mengembalikan -1dan 1untuk sebelum dan sesudah, bukan falseatau 0sebagai mentah a[0] > b[0]karena itu diperlakukan sama:

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
Jawaban apa, kita harus memperbaiki mekanisme penemuan SO. Sesuatu yang sebagus ini seharusnya tidak hilang di sini.
serraosays

30

Konversikan Mapke array menggunakan Array.from, urutkan array, konversi kembali ke Map, misalnya

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()tidak berhasil untuk saya, tetapi Array.from(map.values()).sort()berhasil
Rob Juurlink

1
Ini benar-benar membantu saya, tanpa Array.from, Typecript memberi saya kesalahan kompilasi.
icSapper

7

Idenya adalah mengekstrak kunci peta Anda ke dalam array. Sortir larik ini. Kemudian lakukan iterasi pada larik yang diurutkan ini, dapatkan pasangan nilainya dari peta yang tidak disortir dan masukkan ke dalam peta baru. Peta baru akan diurutkan. Kode di bawah ini adalah implementasinya:

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
Kunci yang tidak disortir dapat ditentukan hanya dengan menggunakan unsortedMap.keys(). Juga keys.sort().map...harus keys.sort().forEach....
faintsignal

5

Anda dapat mengonversi ke array dan memanggil metode penyortiran array di atasnya:

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

3
Dan apa? Anda mendapatkan array bukan peta atau objek
Hijau

5
dan Anda kehilangan kuncinya
mengerem


3

Salah satu caranya adalah dengan mendapatkan array entri, mengurutkannya, dan kemudian membuat Map baru dengan array yang diurutkan:

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

Tetapi jika Anda tidak ingin membuat objek baru, tetapi mengerjakan objek yang sama, Anda dapat melakukan sesuatu seperti ini:

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

Cuplikan di bawah ini mengurutkan peta yang diberikan berdasarkan kuncinya dan memetakan kembali kunci tersebut ke objek nilai kunci. Saya menggunakan fungsi localeCompare karena peta saya adalah 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;
        });

hasil: [{t:'tt'}, {x:'xx'}, {y: 'yy'}];


1

Sejauh yang saya lihat, saat ini tidak mungkin mengurutkan Peta dengan benar.

Solusi lain di mana Peta diubah menjadi larik dan diurutkan dengan cara ini memiliki bug berikut:

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}

Penyortiran membuat objek baru dan semua petunjuk lain ke objek yang tidak diurutkan rusak.


1

2 jam dihabiskan untuk membahas detail.

Perhatikan bahwa jawaban untuk pertanyaan sudah diberikan di https://stackoverflow.com/a/31159284/984471

Namun, pertanyaannya memiliki kunci yang tidak biasa,
Contoh yang jelas & umum dengan penjelasan, di bawah ini memberikan beberapa kejelasan lagi:

.

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

Semoga membantu.


0

Mungkin contoh yang lebih realistis tentang tidak menyortir objek Peta tetapi mempersiapkan penyortiran di depan sebelum melakukan Peta. Sintaksnya menjadi cukup ringkas jika Anda melakukannya seperti ini. Anda dapat menerapkan pengurutan sebelum fungsi peta seperti ini, dengan fungsi pengurutan sebelum peta (Contoh dari aplikasi React yang saya kerjakan menggunakan sintaks JSX)

Tandai bahwa saya di sini mendefinisikan fungsi penyortiran di dalam menggunakan fungsi panah yang mengembalikan -1 jika lebih kecil dan 0 jika tidak diurutkan pada properti objek Javascript dalam larik yang saya dapatkan dari 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
Meskipun ini mungkin menjawab pertanyaan penulis, ini kekurangan beberapa kata yang menjelaskan dan tautan ke dokumentasi. Potongan kode mentah tidak terlalu membantu tanpa beberapa frase di sekitarnya. Harap edit jawaban Anda.
hellow

2
Juga, ini adalah pertanyaan berusia 3 tahun. Menurut Anda, apa yang ditawarkan jawaban Anda yang belum dibahas oleh jawaban lain?
hellow
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.