Ist es möglich, ein ES6-Kartenobjekt zu sortieren?

89

Ist es möglich, die Einträge eines es6-Kartenobjekts zu sortieren?

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

Ergebnisse in:

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

Ist es möglich, die Einträge nach ihren Schlüsseln zu sortieren?

map.entries = {
    0: {"0-1", bar },
    1: {"2-1", foo }
}
Ivan Bacher
quelle
4
Karten sind von Natur aus nicht geordnet (mit Ausnahme der iterierten Einfügereihenfolge, die sortiert und dann [erneut] hinzugefügt werden muss).
user2864740
1
Sortieren Sie die Einträge, wenn Sie über die Karte iterieren (dh sie in ein Array verwandeln)
Halcyon

Antworten:

133

Laut MDN-Dokumentation:

Ein Map-Objekt iteriert seine Elemente in Einfügereihenfolge.

Sie könnten es so machen:

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)

Denken Sie .sort()bei daran, dass das Array nach dem Unicode-Codepunktwert jedes Zeichens sortiert ist, entsprechend der Zeichenfolgenkonvertierung jedes Elements. Also 2-1, 0-1, 3-1wird richtig sortiert.

Walter Chapilliquen - wZVanG
quelle
7
Sie können diese Funktion (a, b) auf Folgendes verkürzen: var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));Verwenden der Pfeilfunktion (Lambda)
Jimmy Chandra
2
In der Tat ist es akut lexikalisch 2-1,foomit 0-1,barund3-1,baz
Bergi
18
@ JimmyChandra: Nicht benutzen (a,b) => a[0] > b[0]!
Bergi
2
Die ...Punkte sind wichtig, sonst versuchen Sie, einen MapIterator zu sortieren
Hammelfleisch
2
Wenn es sich bei den Kartenschlüsseln um Zahlen handelt, erhalten Sie ungültige Ergebnisse mit Zahlen 1e-9, die 100in eine sortierte Karte eingefügt werden. Code, der mit Zahlen arbeitet:new Map([...map.entries()].sort((e1, e2) => e1[0] - e2[0]))
cdalxndr
48

Kurze Antwort

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

Wenn wir beispielsweise Wertzeichenfolgen vergleichen, die gleich sein können, übergeben wir eine Sortierfunktion, die auf [1] zugreift und eine Gleichheitsbedingung hat, die 0 zurückgibt:

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

Beim Vergleich von Schlüsselzeichenfolgen, die nicht gleich sein können (identische Zeichenfolgenschlüssel würden sich gegenseitig überschreiben), können wir die Gleichheitsbedingung überspringen. Wir sollten jedoch immer noch explizit -1 zurückgeben, da a[0] > b[0]die falsche Rückgabe eines Lazy falsch ist (behandelt als 0, dh gleich), wenn a[0] < b[0]:

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

Im Detail mit Beispielen

Das .entries()In [...map.entries()](in vielen Antworten vorgeschlagen) ist redundant und fügt wahrscheinlich eine zusätzliche Iteration der Karte hinzu, es sei denn, die JS-Engine optimiert dies für Sie.

Im einfachen Testfall können Sie tun, was die Frage verlangt:

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

... die, wenn alle Schlüssel Zeichenfolgen sind, gequetschte und erzwungene durch Kommas verbundene Schlüsselwertzeichenfolgen wie '2-1,foo'und vergleichen und '0-1,[object Object]'eine neue Map mit der neuen Einfügereihenfolge zurückgeben:

Hinweis: Wenn Sie nur {}in der Konsolenausgabe von SO sehen, schauen Sie in Ihre echte Browserkonsole

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

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

JEDOCH , es ist keine gute Praxis auf Zwang und stringification wie diese zu verlassen. Sie können Überraschungen bekommen wie:

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

Fehler wie diese sind wirklich schwer zu debuggen - riskieren Sie es nicht!

Wenn Sie nach Schlüsseln oder Werten sortieren möchten, greifen Sie am besten explizit mit a[0]und b[0]in der Sortierfunktion wie folgt darauf zu. Beachten Sie, dass wir zurückkehren sollten -1und 1für vorher und nachher, nicht falseoder 0wie bei raw, a[0] > b[0]da dies als gleich behandelt wird:

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

user56reinstatemonica8
quelle
11
Was für eine Antwort, wir müssen SO-Erkennungsmechanismen reparieren. Etwas so Gutes sollte hier unten nicht verloren gehen.
Serraosays
30

In Mapein Array konvertieren mit Array.from, Array sortieren, zurück konvertieren in Mapz

new Map(
  Array
    .from(eventsByDate)
    .sort((a, b) => {
      // a[0], b[0] is the key of the map
      return a[0] - b[0];
    })
)
Gajus
quelle
1
[...map.values()].sort()nicht für mich arbeiten, aber Array.from(map.values()).sort()tat
Rob Juurlink
1
Dies hat mir tatsächlich geholfen, ohne Array.from gab mir Typescript einen Kompilierungsfehler.
icSapper
7

Die Idee ist, die Schlüssel Ihrer Karte in ein Array zu extrahieren. Sortieren Sie dieses Array. Durchlaufen Sie dann dieses sortierte Array, holen Sie das Wertepaar aus der unsortierten Karte und fügen Sie sie in eine neue Karte ein. Die neue Karte wird in sortierter Reihenfolge angezeigt. Der folgende Code ist die Implementierung:

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);
Mikematik
quelle
2
Die unsortierten Schlüssel können einfach mit bestimmt werden unsortedMap.keys(). Sollte auch keys.sort().map...sein keys.sort().forEach....
Ohnmachtssignal
5

Sie können in ein Array konvertieren und Array-Soring-Methoden darauf aufrufen:

[...map].sort(/* etc */);
wesbos
quelle
3
Und was? Sie erhalten ein Array, keine Karte oder ein Objekt
Green
5
und Sie verlieren den Schlüssel
braks
3

Leider nicht wirklich in ES6 implementiert. Sie haben diese Funktion mit OrderedMap.sort () von ImmutableJS oder _.sortBy () von Lodash.

devside
quelle
3

Eine Möglichkeit besteht darin, das Eintragsarray abzurufen, zu sortieren und dann eine neue Karte mit dem sortierten Array zu erstellen:

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

Wenn Sie jedoch kein neues Objekt erstellen möchten, sondern an demselben arbeiten möchten, können Sie Folgendes tun:

// 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);
})
Moshe Estroti
quelle
1

Das folgende Snippet sortiert die angegebene Karte nach ihren Schlüsseln und ordnet die Schlüssel erneut Schlüsselwertobjekten zu. Ich habe die Funktion localeCompare verwendet, da meine Map string-> string object map war.

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

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

Abdullah Gürsu
quelle
1

Soweit ich sehe, ist es derzeit nicht möglich, eine Karte richtig zu sortieren.

Die anderen Lösungen, bei denen die Karte in ein Array konvertiert und auf diese Weise sortiert wird, weisen den folgenden Fehler auf:

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}

Die Sortierung erstellt ein neues Objekt und alle anderen Zeiger auf das unsortierte Objekt werden beschädigt.

Lacho Tomov
quelle
1

2 Stunden für Details.

Beachten Sie, dass die Antwort auf die Frage bereits unter https://stackoverflow.com/a/31159284/984471 gegeben ist

Die Frage enthält jedoch Schlüssel, die nicht üblich sind.
Im Folgenden finden Sie ein klares und allgemeines Beispiel mit Erläuterungen, das mehr Klarheit bietet:

.

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

Hoffentlich hilft das.

Manohar Reddy Poreddy
quelle
0

Vielleicht ein realistischeres Beispiel dafür, wie man ein Kartenobjekt nicht sortiert, sondern die Sortierung im Voraus vorbereitet, bevor die Karte erstellt wird. Die Syntax wird tatsächlich ziemlich kompakt, wenn Sie es so machen. Sie können die Sortierung vor der Map-Funktion wie folgt mit einer Sortierfunktion vor der Map anwenden (Beispiel aus einer React-App, an der ich mit JSX-Syntax arbeite)

Markieren Sie, dass ich hier eine Sortierfunktion mit einer Pfeilfunktion definiere, die -1 zurückgibt, wenn sie kleiner und 0 ist, andernfalls sortiert nach einer Eigenschaft der Javascript-Objekte in dem Array, das ich von einer API erhalte.

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>
                    )
Tore Aurstad
quelle
-1
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"}
Apoorva Gupta
quelle
3
Dies könnte zwar die Frage des Autors beantworten, es fehlen jedoch einige erklärende Wörter und Links zur Dokumentation. Rohcode-Schnipsel sind ohne einige Ausdrücke nicht sehr hilfreich. Bitte bearbeiten Sie Ihre Antwort.
Gelb
2
Auch dies ist eine 3 Jahre alte Frage. Was bietet Ihre Antwort Ihrer Meinung nach, was andere Antworten noch nicht behandelt haben?
Gelb