Bir ES6 harita nesnesini sıralamak mümkün mü?


98

Bir es6 harita nesnesinin girişlerini sıralamak mümkün müdür?

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

sonuçlanır:

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

Girişleri anahtarlarına göre sıralamak mümkün mü?

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

4
Haritalar doğası gereği sıralanmaz (sıralama ve ardından [yeniden] ekleme gerektiren yinelenen ekleme sırası hariç).
user2864740

1
Harita üzerinde yinelediğinizde girişleri sıralayın (yani bir diziye çevirin)
Halcyon

Yanıtlar:


140

MDN belgelerine göre:

Bir Map nesnesi, öğelerini ekleme sırasında yineler.

Bunu şu şekilde yapabilirsiniz:

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)

Kullanarak .sort(), dizinin her bir öğenin dize dönüşümüne göre her karakterin Unicode kod noktası değerine göre sıralandığını unutmayın. Bu yüzden 2-1, 0-1, 3-1doğru sıralanacaktır.


7
(a, b) işlevlerini şu şekilde kısaltabilirsiniz: var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));ok işlevini kullanarak (lambda)
Jimmy Chandra

2
Aslında, sözcüksel 2-1,fooolarak 0-1,barve3-1,baz
Bergi


4
...Noktalar önemlidir, aksi takdirde bir MapIterator sıralamak için çalışıyoruz edilir
muttonUp

2
Harita anahtarları sayı ise, sıralı haritada 1e-9peşine konmak gibi sayılarla geçersiz sonuçlar alırsınız 100. Sayılarla çalışan kod:new Map([...map.entries()].sort((e1, e2) => e1[0] - e2[0]))
cdalxndr

60

Kısa cevap

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

Örneğin, eşit olabilecek değer dizelerini karşılaştırarak, [1] 'e erişen ve 0 döndüren eşittir koşuluna sahip bir sıralama işlevi iletiriz:

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

Eşit olamayan anahtar dizelerini karşılaştırarak (aynı dize anahtarları birbirinin üzerine yazabilir), eşittir koşulunu atlayabiliriz. Bununla birlikte, yine de açıkça -1 döndürmeliyiz, çünkü bir tembel döndürmek a[0] > b[0]yanlış olarak yanlış verir (0 olarak kabul edilir, yani eşittir) a[0] < b[0]:

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

Örneklerle ayrıntılı olarak

.entries()İçinde [...map.entries()](birçok cevapları önerilen) muhtemelen uzağa sizin için JS motoru optimize sürece haritanın fazladan iterasyon ekleyerek gereksiz olduğunu.

Basit test durumunda, sorunun istediğini aşağıdakilerle yapabilirsiniz:

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

... hangi tuşlar bütün dizeleri ise, ezilmiş ve virgülle katıldı anahtar-değer gibi dizeleri zorla karşılaştırır '2-1,foo've '0-1,[object Object]'yeni ekleme talimatıyla yeni bir harita dönen,:

Not: Yalnızca {}SO'nun konsol çıktısında görüyorsanız , gerçek tarayıcı konsolunuza bakın

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

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

ANCAK , böyle zorlamaya ve zorlamaya güvenmek iyi bir uygulama değildir. Şunlar gibi sürprizlerle karşılaşabilirsiniz:

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

Bunun gibi hataların ayıklanması gerçekten zordur - riske atmayın!

Anahtarlara veya değerlere göre sıralamak istiyorsanız, bunlara bunun gibi sıralama işlevi ile a[0]ve b[0]içinde açıkça erişmek en iyisidir . Biz döndürmesi gerektiğini Not -1ve 1değil, önce ve sonra falseveya 0çiğ gibi a[0] > b[0]o eşittir olarak kabul edilir çünkü:

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
Ne cevap, SO keşif mekanizmalarını düzeltmeliyiz. Bu kadar iyi bir şey burada kaybolmamalı.
serraosays

30

MapKullanarak bir diziye dönüştürün , diziyi Array.fromsıralayın, geri dönüştürün Map, örneğin

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()benim için çalışmadı, ama işe Array.from(map.values()).sort()yaradı
Rob Juurlink

2
Bu aslında Array.from olmadan bana yardımcı oldu, Typescript bana bir derleme hatası verdi.
icSapper

7

Buradaki fikir, haritanızın anahtarlarını bir diziye çıkarmaktır. Bu diziyi sıralayın. Sonra bu sıralı diziyi yineleyin, sıralanmamış haritadan değer çiftini alın ve bunları yeni bir haritaya yerleştirin. Yeni harita sıralı bir düzende olacaktır. Aşağıdaki kod, uygulamasıdır:

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
Sıralanmamış anahtarlar kullanılarak kolayca belirlenebilir unsortedMap.keys(). Ayrıca keys.sort().map...olmalı keys.sort().forEach....
faintsignal

5

Bir diziye dönüştürebilir ve üzerinde dizi sıralama yöntemlerini çağırabilirsiniz:

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

3
Ve ne? Harita veya nesne olmayan bir dizi alıyorsunuz
Green

5
ve anahtarı kaybedersiniz
56'da frenler


3

Bunun bir yolu, girdiler dizisini elde etmek, sıralamak ve ardından sıralanmış diziyle yeni bir Harita oluşturmaktır:

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

Ancak yeni bir nesne oluşturmak istemiyor, ancak aynı nesne üzerinde çalışmak istiyorsanız, şöyle bir şey yapabilirsiniz:

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

Aşağıdaki kod parçası, verilen haritayı anahtarlarına göre sıralar ve anahtarları yeniden anahtar-değer nesnelerine eşler. Haritam string-> string nesne haritası olduğundan localeCompare işlevini kullandım.

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

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


1

Gördüğüm kadarıyla bir Haritayı düzgün bir şekilde sıralamak şu anda mümkün değil.

Haritanın bir diziye dönüştürüldüğü ve bu şekilde sıralandığı diğer çözümler aşağıdaki hataya sahiptir:

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}

Sıralama, yeni bir nesne oluşturur ve sıralanmamış nesneye yönelik diğer tüm işaretçiler bozulur.


1

Ayrıntılara girmek için 2 saat harcandı.

Sorunun cevabının zaten https://stackoverflow.com/a/31159284/984471 adresinde verildiğini unutmayın.

Ancak, soru zamanki olanlar değildir tuşları bulunuyor
A , açıklama ile net ve genel örneği bulunmaktadır yani biraz daha netlik sağlamaktadır:

.

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

Umarım yardımcı olur.


0

Belki bir Harita nesnesini sıralamakla değil, Harita'yı yapmadan önce sıralamayı önceden hazırlamakla ilgili daha gerçekçi bir örnek. Böyle yaparsanız sözdizimi aslında oldukça derli toplu hale gelir. Eşlemeden önce bir sıralama işlevi ile harita işlevinden önce sıralamayı bu şekilde uygulayabilirsiniz (JSX sözdizimini kullanarak üzerinde çalıştığım bir React uygulamasından örnek)

Burada, bir API'den aldığım dizideki Javascript nesnelerinin bir özelliğine göre daha küçükse -1 ve 0 ise başka şekilde sıralanırsa -1 döndüren bir ok işlevini kullanarak içinde bir sıralama işlevi tanımladığımı işaretleyin.

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
Bu, yazarların sorusunu yanıtlasa da, bazı açıklayıcı sözcüklerden ve belgelere bağlantılardan yoksundur. Ham kod parçacıkları, etrafında bazı ifadeler olmadan pek yardımcı olmaz. Lütfen cevabınızı düzenleyin.
hellow

2
Ayrıca bu 3 yıllık bir sorudur. Sence cevabınız, diğer cevapların henüz kapsamadığı neyi sunuyor?
hellow
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.