Ich versuche, ein Array mit Objekten basierend auf mehreren Attributen zu sortieren. Wenn also das erste Attribut zwischen zwei Objekten gleich ist, sollte ein zweites Attribut verwendet werden, um die beiden Objekte zu kombinieren. Betrachten Sie zum Beispiel das folgende Array:
var patients = [
[{name: 'John', roomNumber: 1, bedNumber: 1}],
[{name: 'Lisa', roomNumber: 1, bedNumber: 2}],
[{name: 'Chris', roomNumber: 2, bedNumber: 1}],
[{name: 'Omar', roomNumber: 3, bedNumber: 1}]
];
Wenn Sie diese nach dem roomNumber
-Attribut sortieren, würde ich folgenden Code verwenden:
var sortedArray = _.sortBy(patients, function(patient) {
return patient[0].roomNumber;
});
Das funktioniert gut, aber wie gehe ich vor, damit 'John' und 'Lisa' richtig sortiert werden?
sortBy
sagt, dass es sich um einen stabilen Sortieralgorithmus handelt. Sie sollten in der Lage sein, zuerst nach Ihrer zweiten Eigenschaft zu sortieren und dann erneut nach Ihrer ersten Eigenschaft zu sortieren:
var sortedArray = _(patients).chain().sortBy(function(patient) {
return patient[0].name;
}).sortBy(function(patient) {
return patient[0].roomNumber;
}).value();
Wenn die zweite sortBy
feststellt, dass John und Lisa dieselbe Raumnummer haben, werden sie in der Reihenfolge gehalten, in der sie gefunden wurden, wobei die erste sortBy
auf "Lisa, John" eingestellt war.
Hier ist ein harter Trick, den ich manchmal in diesen Fällen benutze: Kombiniere die Eigenschaften so, dass das Ergebnis sortierbar ist:
var sortedArray = _.sortBy(patients, function(patient) {
return [patient[0].roomNumber, patient[0].name].join("_");
});
Wie gesagt, das ist ziemlich hackig. Um dies richtig zu machen, möchten Sie wahrscheinlich die JavaScript sort
-Kernmethode tatsächlich verwenden:
patients.sort(function(x, y) {
var roomX = x[0].roomNumber;
var roomY = y[0].roomNumber;
if (roomX !== roomY) {
return compare(roomX, roomY);
}
return compare(x[0].name, y[0].name);
});
// General comparison function for convenience
function compare(x, y) {
if (x === y) {
return 0;
}
return x > y ? 1 : -1;
}
Natürlich sortiert this Ihr Array an Ort und Stelle. Wenn Sie eine sortierte Kopie wünschen (wie _.sortBy
Sie angeben würde), klonen Sie zuerst das Array:
function sortOutOfPlace(sequence, sorter) {
var copy = _.clone(sequence);
copy.sort(sorter);
return copy;
}
Aus Langeweile habe ich auch eine allgemeine Lösung geschrieben (um nach einer beliebigen Anzahl von Schlüsseln zu sortieren), auch hier: schauen Sie .
Ich weiß, dass ich zu spät zur Party komme, aber ich wollte dies für diejenigen hinzufügen, die eine sauberere und schnellere Lösung benötigen, die bereits vorgeschlagen wurde. Sie können sortBy-Aufrufe in der Reihenfolge der unwichtigsten Eigenschaft an die wichtigste Eigenschaft ketten. Im folgenden Code erstelle ich ein neues Array von Patienten, sortiert nach Name in RoomNumber aus dem ursprünglichen Array mit dem Namen patient .
var sortedPatients = _.chain(patients)
.sortBy('Name')
.sortBy('RoomNumber')
.value();
ihr Initialisierer für Patienten ist zwar ein bisschen komisch, oder? Warum nicht Sie initialisieren Sie diese Variable so - als wahres Array von Objekten - Sie können dies mit _.flatten () und nicht als Array von Arrays einzelner Objekte tun. Vielleicht handelt es sich um Tippfehler):
var patients = [
{name: 'Omar', roomNumber: 3, bedNumber: 1},
{name: 'John', roomNumber: 1, bedNumber: 1},
{name: 'Chris', roomNumber: 2, bedNumber: 1},
{name: 'Lisa', roomNumber: 1, bedNumber: 2},
{name: 'Kiko', roomNumber: 1, bedNumber: 2}
];
Ich habe die Liste anders sortiert und fügte Kiko in Lisas Bett hinzu; Nur zum Spaß und sehen, welche Änderungen gemacht würden ...
var sorted = _(patients).sortBy(
function(patient){
return [patient.roomNumber, patient.bedNumber, patient.name];
});
inspiziert sortiert und du siehst das
[
{bedNumber: 1, name: "John", roomNumber: 1},
{bedNumber: 2, name: "Kiko", roomNumber: 1},
{bedNumber: 2, name: "Lisa", roomNumber: 1},
{bedNumber: 1, name: "Chris", roomNumber: 2},
{bedNumber: 1, name: "Omar", roomNumber: 3}
]
meine Antwort ist also: Verwenden Sie ein Array in Ihrer Rückruffunktion Dies ist der Antwort von Dan Tao sehr ähnlich. Ich vergesse nur den Join (vielleicht, weil ich das Array von Unique-Arrays entfernt habe.) Artikel :))
Wenn Sie Ihre Datenstruktur verwenden, wäre dies:
var sorted = _(patients).chain()
.flatten()
.sortBy( function(patient){
return [patient.roomNumber,
patient.bedNumber,
patient.name];
})
.value();
und ein testload wäre interessant ...
Keine dieser Antworten ist ideal als allgemeine Methode zur Verwendung mehrerer Felder in einer Sortierung. Alle oben genannten Ansätze sind ineffizient, da sie entweder das Array mehrmals sortieren müssen (was bei einer ausreichend großen Liste die Abläufe erheblich verlangsamen könnte) oder sie generieren eine große Anzahl von Müllobjekten, die der VM bereinigen muss (und letztendlich das Programm verlangsamen).
Hier ist eine Lösung, die schnell und effizient ist und leicht eine umgekehrte Sortierung ermöglicht. Sie kann mit underscore
oder lodash
oder direkt mit Array.sort
verwendet werden.
Der wichtigste Teil ist die compositeComparator
-Methode, die ein Array von Komparatorfunktionen verwendet und eine neue Komparatorfunktion zurückgibt.
/**
* Chains a comparator function to another comparator
* and returns the result of the first comparator, unless
* the first comparator returns 0, in which case the
* result of the second comparator is used.
*/
function makeChainedComparator(first, next) {
return function(a, b) {
var result = first(a, b);
if (result !== 0) return result;
return next(a, b);
}
}
/**
* Given an array of comparators, returns a new comparator with
* descending priority such that
* the next comparator will only be used if the precending on returned
* 0 (ie, found the two objects to be equal)
*
* Allows multiple sorts to be used simply. For example,
* sort by column a, then sort by column b, then sort by column c
*/
function compositeComparator(comparators) {
return comparators.reduceRight(function(memo, comparator) {
return makeChainedComparator(comparator, memo);
});
}
Sie benötigen auch eine Vergleichsfunktion, um die Felder zu vergleichen, nach denen Sie sortieren möchten. Die Funktion naturalSort
erstellt einen Komparator für ein bestimmtes Feld. Das Schreiben eines Komparators für die Rückwärtssortierung ist ebenfalls trivial.
function naturalSort(field) {
return function(a, b) {
var c1 = a[field];
var c2 = b[field];
if (c1 > c2) return 1;
if (c1 < c2) return -1;
return 0;
}
}
(Der gesamte Code ist bisher wiederverwendbar und könnte zum Beispiel im Utility-Modul aufbewahrt werden.)
Als Nächstes müssen Sie den Verbundvergleicher erstellen. Für unser Beispiel würde es so aussehen:
var cmp = compositeComparator([naturalSort('roomNumber'), naturalSort('name')]);
Dies wird nach Raumnummer sortiert, gefolgt vom Namen. Das Hinzufügen zusätzlicher Sortierkriterien ist trivial und hat keinen Einfluss auf die Leistung der Sortierung.
var patients = [
{name: 'John', roomNumber: 3, bedNumber: 1},
{name: 'Omar', roomNumber: 2, bedNumber: 1},
{name: 'Lisa', roomNumber: 2, bedNumber: 2},
{name: 'Chris', roomNumber: 1, bedNumber: 1},
];
// Sort using the composite
patients.sort(cmp);
console.log(patients);
Gibt Folgendes zurück
[ { name: 'Chris', roomNumber: 1, bedNumber: 1 },
{ name: 'Lisa', roomNumber: 2, bedNumber: 2 },
{ name: 'Omar', roomNumber: 2, bedNumber: 1 },
{ name: 'John', roomNumber: 3, bedNumber: 1 } ]
Ich bevorzuge diese Methode deshalb, weil sie eine schnelle Sortierung nach einer beliebigen Anzahl von Feldern zulässt, nicht viel Abfall erzeugt oder String-Verkettungen innerhalb der Sortierung durchführt und leicht verwendet werden kann, so dass einige Spalten umgekehrt sortiert werden, während die Reihenfolge der Spalten normalerweise ist Sortieren.
Einfaches Beispiel from http://janetriley.net/2014/12/sort-on-multiple-keys-with-underscores-sortby.html (mit freundlicher Genehmigung von @MikeDevenney)
Code
var FullySortedArray = _.sortBy(( _.sortBy(array, 'second')), 'first');
Mit Ihren Daten
var FullySortedArray = _.sortBy(( _.sortBy(patients, 'roomNumber')), 'name');
Vielleicht sind Underscore.js oder nur Javascript-Engines jetzt anders als zu dem Zeitpunkt, als diese Antworten geschrieben wurden, aber ich konnte dieses Problem lösen, indem ich einfach ein Array der Sortierschlüssel zurückgab.
var input = [];
for (var i = 0; i < 20; ++i) {
input.Push({
a: Math.round(100 * Math.random()),
b: Math.round(3 * Math.random())
})
}
var output = _.sortBy(input, function(o) {
return [o.b, o.a];
});
// output is now sorted by b ascending, a ascending
In Aktion sehen Sie bitte diese Geige: https://jsfiddle.net/mikeular/xenu3u91/
Einfach ein Array von Eigenschaften zurückgeben mit dem Sie sortieren möchten:
ES6-Syntax
var sortedArray = _.sortBy(patients, patient => [patient[0].name, patient[1].roomNumber])
ES5-Syntax
var sortedArray = _.sortBy(patients, function(patient) {
return [patient[0].name, patient[1].roomNumber]
})
Dies hat keine Nebeneffekte bei der Umwandlung einer Zahl in eine Zeichenfolge.
Sie können die Eigenschaften, die Sie sortieren möchten, im Iterator verketten:
return [patient[0].roomNumber,patient[0].name].join('|');
oder etwas gleichwertiges.
HINWEIS: Da Sie das numerische Attribut roomNumber in eine Zeichenfolge konvertieren, müssen Sie etwas tun, wenn Sie Raumnummern> 10 hätten. Andernfalls wird 11 vor 2 angezeigt 1.
Ich denke, Sie sollten lieber _.orderBy
anstelle von sortBy
verwenden:
_.orderBy(patients, ['name', 'roomNumber'], ['asc', 'desc'])
Wenn Sie Angular verwenden, können Sie den Nummernfilter in der HTML-Datei verwenden, anstatt JS- oder CSS-Handler hinzuzufügen. Zum Beispiel:
No fractions: <span>{{val | number:0}}</span><br>
In diesem Beispiel wird val = 1234567 als angezeigt
No fractions: 1,234,567
Beispiel und weitere Anleitungen unter: https://docs.angularjs.org/api/ng/filter/number