Sortieren Sie das Array von Objekten nach dem Wert der Zeichenfolgeeigenschaft

2763

Ich habe eine Reihe von JavaScript-Objekten:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

Wie kann ich sie nach dem Wert von last_nomin JavaScript sortieren ?

Ich weiß Bescheid sort(a,b), aber das scheint nur bei Zeichenfolgen und Zahlen zu funktionieren. Muss ich toString()meinen Objekten eine Methode hinzufügen ?

Tyrone Slothrop
quelle
7
Mit diesem Skript können Sie genau das tun, es sei denn, Sie möchten Ihre eigene Vergleichsfunktion oder Ihren eigenen Sortierer schreiben: thomasfrank.se/sorting_things.html
Baversjo
Am schnellsten ist es, das isomorphe Sort-Array- Modul zu verwenden, das sowohl im Browser als auch im Knoten nativ funktioniert und jede Art von Eingabe, berechneten Feldern und benutzerdefinierten Sortierreihenfolgen unterstützt.
Lloyd

Antworten:

3920

Es ist einfach genug, eine eigene Vergleichsfunktion zu schreiben:

function compare( a, b ) {
  if ( a.last_nom < b.last_nom ){
    return -1;
  }
  if ( a.last_nom > b.last_nom ){
    return 1;
  }
  return 0;
}

objs.sort( compare );

Oder inline (c / o Marco Demaio):

objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0)); 
Wogan
quelle
441
Oder inline: objs.sort (Funktion (a, b) {return (a.last_nom> b.last_nom)? 1: ((b.last_nom> a.last_nom)? -1: 0);});
Marco Demaio
176
return a.last_nom.localeCompare(b.last_nom)wird auch funktionieren.
Cerbrus
146
für diejenigen, die nach einer Sorte suchen, bei der das Feld numerisch ist, der Vergleichsfunktionskörper: return a.value - b.value;(ASC)
Andre Figueiredo
20
@Cerbrus localeCompareist wichtig, wenn Akzentzeichen in Fremdsprachen verwendet werden, und auch eleganter.
Marcos Lima
839

Sie können auch eine dynamische Sortierfunktion erstellen, die Objekte nach ihrem übergebenen Wert sortiert:

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        /* next line works with strings and numbers, 
         * and you may want to customize it to your needs
         */
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

Sie können also eine Reihe von Objekten wie folgt haben:

var People = [
    {Name: "Name", Surname: "Surname"},
    {Name:"AAA", Surname:"ZZZ"},
    {Name: "Name", Surname: "AAA"}
];

... und es wird funktionieren, wenn Sie:

People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));

Eigentlich beantwortet dies bereits die Frage. Der folgende Teil wurde geschrieben, weil viele Leute mich kontaktiert haben und sich beschwert haben, dass es nicht mit mehreren Parametern funktioniert .

Mehrere Parameter

Mit der folgenden Funktion können Sie Sortierfunktionen mit mehreren Sortierparametern generieren.

function dynamicSortMultiple() {
    /*
     * save the arguments object as it will be overwritten
     * note that arguments object is an array-like object
     * consisting of the names of the properties to sort by
     */
    var props = arguments;
    return function (obj1, obj2) {
        var i = 0, result = 0, numberOfProperties = props.length;
        /* try getting a different result from 0 (equal)
         * as long as we have extra properties to compare
         */
        while(result === 0 && i < numberOfProperties) {
            result = dynamicSort(props[i])(obj1, obj2);
            i++;
        }
        return result;
    }
}

Was würde es Ihnen ermöglichen, so etwas zu tun:

People.sort(dynamicSortMultiple("Name", "-Surname"));

Unterklassen-Array

Für die Glücklichen unter uns, die ES6 verwenden können, um die nativen Objekte zu erweitern:

class MyArray extends Array {
    sortBy(...args) {
        return this.sort(dynamicSortMultiple.apply(null, args));
    }
}

Das würde dies ermöglichen:

MyArray.from(People).sortBy("Name", "-Surname");
Ege Özcan
quelle
Beachten Sie, dass Eigenschaftsnamen in JavaScript eine beliebige Zeichenfolge sein können. Wenn Sie Eigenschaften haben, die mit einem "-" beginnen (äußerst unwahrscheinlich und wahrscheinlich keine gute Idee), müssen Sie die dynamicSort-Funktion ändern, um etwas anderes als umgekehrte Sortierung zu verwenden Indikator.
Ege Özcan
Ich habe festgestellt, dass dynamicSort()im obigen Beispiel Großbuchstaben vor Kleinbuchstaben stehen. Wenn ich die Werte zum Beispiel haben APd, Aklinund Abe- die Ergebnisse in einer ASC sollte Art sein Abe, Aklin, APd. Aber mit Ihrem Beispiel sind die Ergebnisse APd, Abe, Aklin. Wie auch immer, um dieses Verhalten zu korrigieren?
Lloyd Banks
6
@LloydBanks Wenn Sie dies ausschließlich für Zeichenfolgen verwenden, können Sie var result = a[property].localeCompare(b[property]);stattdessen anstelle von verwenden var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;.
Ege Özcan
1
@ EgeÖzcan Es sollte entweder Elemente nach oben oder unten gesetzt, hier der Umsetzung Ich habe am Ende mit pastebin.com/g4dt23jU
dzh
1
Dies ist eine großartige Lösung, hat aber ein Problem, wenn Sie Zahlen vergleichen. Bitte fügen Sie dies vor der Überprüfung hinzu:if( !isNaN(a[property]) ) a[property] = Number(a[property]); if( !isNaN(b[property]) ) b[property] = Number(b[property]);
Ivijan Stefan Stipić
416

In ES6 / ES2015 oder höher können Sie folgende Schritte ausführen:

objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));

Vor ES6 / ES2015

objs.sort(function(a, b) {
    return a.last_nom.localeCompare(b.last_nom)
});
Vlad Bezden
quelle
29
Dies ist seit JS 1.1 verfügbar. Das fette Pfeilstück dazu ist das Stück ES6 / 2015. Aber immer noch sehr nützlich und meiner Meinung nach die beste Antwort
Jon Harding
13
@PratikKelwalkar: Wenn Sie eine Umkehrung benötigen, wechseln Sie einfach den Vergleich zwischen a und b: objs.sort ((a, b) => b.last_nom.localeCompare (a.last_nom));
Vlad Bezden
Ist es möglich, auch einen Index zu verwenden, um das Feld für die Sortierung zu adressieren: Anstatt last_nomnur die Nummer im Array zu verwenden : 1?
and-bri
4
@VladBezden danke für deine Antwort! Diese Lösung ist die erste mit sehr geringem programmatischem Aufwand und korrekten Sortierergebnissen und einem String-Array wie: ["Name1", "Name10", "Name2", "etwas anderes", "Name11"]. Ich habe Sortieren, um richtig mit zu arbeitenobjs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true}));
scipper
1
Um dies mit absteigenden Zahlen zu tun: .sort ((a, b) => b.numberProperty - a.numberProperty). Aufsteigend: .sort ((a, b) => a.numberProperty - b.numberProperty)
Sebastian Patten
188

underscore.js

Verwenden Sie Unterstrich, es ist klein und super ...

sortBy_.sortBy (Liste, Iterator, [Kontext]) Gibt eine sortierte Kopie der Liste zurück, die in aufsteigender Reihenfolge nach den Ergebnissen der Ausführung jedes Werts durch den Iterator geordnet ist. Iterator kann auch der Zeichenfolgenname der Eigenschaft sein, nach der sortiert werden soll (z. B. Länge).

var objs = [ 
  { first_nom: 'Lazslo',last_nom: 'Jamf' },
  { first_nom: 'Pig', last_nom: 'Bodine'  },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

var sortedObjs = _.sortBy( objs, 'first_nom' );
David Morrow
quelle
18
David, könnten Sie die Antwort bearbeiten, um zu sagen , var sortedObjs = _.sortBy( objs, 'first_nom' );. objswird dadurch nicht selbst sortiert. Die Funktion wird das Rück ein sortiertes Array. Das würde es expliziter machen.
Jess
10
Um die Sortierung umzukehren:var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();
Erdal G.
2
Sie müssen die Javascript-Bibliothek "Unterstrich" laden:<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script>
und-bri
5
Auch verfügbar Lodashfür diejenigen, die dieses bevorzugen
WoJ
2
In lodash wäre dies das gleiche: var sortedObjs = _.sortBy( objs, 'first_nom' );oder wenn Sie es in einer anderen Reihenfolge wollen:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );
Travis Heeter
186

Verstehe nicht, warum die Leute es so kompliziert machen:

objs.sort(function(a, b){
  return a.last_nom > b.last_nom;
});

Für strengere Motoren:

objs.sort(function(a, b){
  return a.last_nom == b.last_nom ? 0 : +(a.last_nom > b.last_nom) || -1;
});

Tauschen Sie den Operator aus, um ihn in umgekehrter alphabetischer Reihenfolge sortieren zu lassen.

p3lim
quelle
17
Dies ist eigentlich nicht korrekt, da die in sort verwendete Funktion -1, 0 oder 1 zurückgeben sollte, die obige Funktion jedoch einen Booleschen Wert zurückgibt. Die Sortierung funktioniert gut in Chrome, schlägt jedoch beispielsweise in PhantomJS fehl. Siehe code.google.com/p/phantomjs/issues/detail?id=1090
schup
Einige Motoren sind für Dummheit verantwortlich, diese Art hat das ausgenutzt. Ich habe meine Antwort mit einer richtigen Version aktualisiert.
p3lim
19
Ich würde vorschlagen, die erste Version zu bearbeiten. Es ist prägnanter, sieht also attraktiver aus, funktioniert aber nicht, zumindest nicht zuverlässig. Wenn jemand es in einem Browser versucht und es funktioniert, merkt er möglicherweise nicht einmal, dass er ein Problem hat (insbesondere, wenn er die Kommentare nicht gelesen hat). Die zweite Version funktioniert ordnungsgemäß, sodass die erste wirklich nicht erforderlich ist.
Kate
2
@Simon Ich habe es sehr geschätzt, zuerst die "etwas falsche" Version zu haben, da die strengere Implementierung einige Sekunden zum Parsen und Verstehen benötigt und ohne sie viel schwieriger wäre.
Aaron Sherman
1
@ Lion789 mach einfach das:if(a.count == b.count) return a.name > b.name; else return a.count > b.count;
p3lim
62

Wenn Sie doppelte Nachnamen haben, können Sie diese nach Vornamen sortieren.

obj.sort(function(a,b){
  if(a.last_nom< b.last_nom) return -1;
  if(a.last_nom >b.last_nom) return 1;
  if(a.first_nom< b.first_nom) return -1;
  if(a.first_nom >b.first_nom) return 1;
  return 0;
});
kennebec
quelle
@BadFeelingAboutThis was bedeutet es, entweder -1 oder 1 zurückzugeben? Ich verstehe, dass -1 wörtlich bedeutet, dass A nur durch die Syntax kleiner als B ist, aber warum eine 1 oder -1 verwenden? Ich sehe, dass jeder diese Zahlen als Rückgabewerte verwendet, aber warum? Vielen Dank.
Chris22
1
@ Chris22 eine negative Zahl zurück Mittel , die bnach kommen sollte ain dem Array. Wenn eine positive Zahl zurückgegeben wird, bedeutet dies, adass danach kommen sollte b. Wenn 0zurückgegeben wird, bedeutet dies, dass sie als gleich angesehen werden. Sie können immer die Dokumentation lesen: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
BadFeelingAboutThis
@BadFeelingAboutDieser Dank für die Erklärung und den Link. Ob Sie es glauben oder nicht, ich habe verschiedene Codeausschnitte mit dem gegoogelt, 1, 0, -1bevor ich dies hier gefragt habe. Ich habe einfach nicht die Informationen gefunden, die ich brauchte.
Chris22
48

Einfache und schnelle Lösung dieses Problems mithilfe der Vererbung von Prototypen:

Array.prototype.sortBy = function(p) {
  return this.slice(0).sort(function(a,b) {
    return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
  });
}

Beispiel / Verwendung

objs = [{age:44,name:'vinay'},{age:24,name:'deepak'},{age:74,name:'suresh'}];

objs.sortBy('age');
// Returns
// [{"age":24,"name":"deepak"},{"age":44,"name":"vinay"},{"age":74,"name":"suresh"}]

objs.sortBy('name');
// Returns
// [{"age":24,"name":"deepak"},{"age":74,"name":"suresh"},{"age":44,"name":"vinay"}]

Update: Ändert das ursprüngliche Array nicht mehr.

Vinay Aggarwal
quelle
5
Es wird nicht nur ein anderes Array zurückgegeben. aber sortiert tatsächlich das Original!.
Vinay Aggarwal
Wenn Sie sicherstellen möchten, dass Sie eine natürliche Sortierung mit Zahlen verwenden (z. B. 0,1,2,10,11 usw.), verwenden Sie parseInt mit dem Radix-Satz. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… also: return (parseInt (a [p], 10)> parseInt (b [p], 10))? 1: (parseInt (a [p], 10) <parseInt (b [p], 10))? -1: 0;
Paul
@codehuntr Danke, dass du es korrigiert hast. Aber ich denke, anstatt eine Sortierfunktion für diese Sensibilisierung zu erstellen, ist es besser, wenn wir eine separate Funktion zum Fixieren von Datentypen erstellen. Weil die Sortierfunktion nicht sagen kann, welche Eigenschaft welche Art von Daten enthält. :)
Vinay Aggarwal
Sehr schön. Kehren Sie die Pfeile um, um einen Auf- / Abstiegseffekt zu erzielen.
Abdul Sadik Yalcin
4
Schlagen Sie
39

Ab 2018 gibt es eine viel kürzere und elegantere Lösung. Benutz einfach. Array.prototype.sort () .

Beispiel:

var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];

// sort by value
items.sort(function (a, b) {
  return a.value - b.value;
});
0leg
quelle
7
In der Frage wurden zum Vergleich Zeichenfolgen zum Vergleich verwendet. Ihre Antwort eignet sich hervorragend zum Sortieren nach Zahlen, ist jedoch nicht so gut für Vergleiche nach Zeichenfolgen.
Smcstewart
Die a.value - b.valuezum Vergleichen der Objektattribute ( in diesem Fall Zahlen ) verwendeten können für die verschiedenen Datenzeiten übernommen werden. Beispielsweise kann Regex verwendet werden, um jedes Paar der benachbarten Zeichenfolgen zu vergleichen .
0leg
1
@ 0leg Ich möchte hier ein Beispiel für die Verwendung von Regex sehen, um Zeichenfolgen auf diese Weise zu vergleichen.
Bob Stein
Diese Implementierung ist recht gut, wenn Sie sie nach ID sortieren müssen. Ja, Sie haben vorgeschlagen, Regex zu verwenden, um benachbarte Zeichenfolgen zu vergleichen, was die Lösung komplizierter macht, wohingegen der Zweck dieser vereinfachten Version anders ist, wenn Regex zusammen mit der angegebenen Lösung verwendet wird. Einfachheit ist das Beste.
Surendrapanday
33

Alte Antwort, die nicht richtig ist:

arr.sort((a, b) => a.name > b.name)

AKTUALISIEREN

Aus Beauchamps Kommentar:

arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))

Besser lesbares Format:

arr.sort((a, b) => {
  if (a.name < b.name) return -1
  return a.name > b.name ? 1 : 0
})

Ohne verschachtelte Ternäre:

arr.sort((a, b) => a.name < b.name ? - 1 : Number(a.name > b.name))

Erläuterung: Number()wird truezu 1und falsezu umgewandelt 0.

Damjan Pavlica
quelle
Es funktioniert, aber das Ergebnis ist aus irgendeinem Grund instabil
TitanFighter
@ AO17 nein wird es nicht. Sie können keine Zeichenfolgen subtrahieren.
Patrick Roberts
15
Dies sollte es tun:arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))
Jean-François Beauchamp
3
@ Jean-FrançoisBeauchamp, Ihre Lösung funktioniert einwandfrei und viel besser.
Ahsan
Der dritte mit Nummer ist unkompliziert und nett!
Kojo
29

Anstatt eine benutzerdefinierte Vergleichsfunktion zu verwenden, können Sie auch einen Objekttyp mit einer benutzerdefinierten toString()Methode erstellen (die von der Standardvergleichsfunktion aufgerufen wird):

function Person(firstName, lastName) {
    this.firtName = firstName;
    this.lastName = lastName;
}

Person.prototype.toString = function() {
    return this.lastName + ', ' + this.firstName;
}

var persons = [ new Person('Lazslo', 'Jamf'), ...]
persons.sort();
Christoph
quelle
29

Lodash.js (Obermenge von Underscore.js )

Es ist gut, nicht für jede einfache Logik ein Framework hinzuzufügen, aber das Verlassen auf gut getestete Utility-Frameworks kann die Entwicklung beschleunigen und die Anzahl der Fehler reduzieren.

Lodash erzeugt sehr sauberen Code und fördert einen funktionaleren Programmierstil . Auf einen Blick wird klar, was die Absicht des Codes ist.

Das Problem von OP kann einfach gelöst werden als:

const sortedObjs = _.sortBy(objs, 'last_nom');

Mehr Info? ZB haben wir folgendes verschachteltes Objekt:

const users = [
  { 'user': {'name':'fred', 'age': 48}},
  { 'user': {'name':'barney', 'age': 36 }},
  { 'user': {'name':'wilma'}},
  { 'user': {'name':'betty', 'age': 32}}
];

Wir können jetzt die Kurzform _.property verwendenuser.age , um den Pfad zu der Eigenschaft anzugeben, die übereinstimmen soll. Wir werden die Benutzerobjekte nach der Eigenschaft des verschachtelten Alters sortieren. Ja, es ermöglicht die Zuordnung verschachtelter Eigenschaften!

const sortedObjs = _.sortBy(users, ['user.age']);

Willst du es umgekehrt? Kein Problem. Verwenden Sie _.reverse .

const sortedObjs = _.reverse(_.sortBy(users, ['user.age']));

Möchten Sie beide mithilfe einer Kette kombinieren ?

const { chain } = require('lodash');
const sortedObjs = chain(users).sortBy('user.age').reverse().value();

Oder wann bevorzugen Sie den Fluss gegenüber der Kette?

const { flow, reverse, sortBy } = require('lodash/fp');
const sortedObjs = flow([sortBy('user.age'), reverse])(users); 
Nico Van Belle
quelle
28

Sie können verwenden

Einfachster Weg: Lodash

( https://lodash.com/docs/4.17.10#orderBy )

Diese Methode ähnelt _.sortBy, ermöglicht jedoch die Angabe der Sortierreihenfolge der Iterate, nach denen sortiert werden soll. Wenn die Reihenfolge nicht angegeben ist, werden alle Werte in aufsteigender Reihenfolge sortiert. Andernfalls geben Sie eine Reihenfolge von "desc" für absteigend oder "asc" für aufsteigende Sortierreihenfolge der entsprechenden Werte an.

Argumente

Sammlung (Array | Objekt): Die Sammlung, über die iteriert werden soll. [iteratees = [_. identity]] (Array [] | Function [] | Object [] | string []): Die zu sortierenden Iteratees. [orders] (string []): Die Sortierreihenfolge von Iteraten.

Kehrt zurück

(Array): Gibt das neue sortierte Array zurück.


var _ = require('lodash');
var homes = [
    {"h_id":"3",
     "city":"Dallas",
     "state":"TX",
     "zip":"75201",
     "price":"162500"},
    {"h_id":"4",
     "city":"Bevery Hills",
     "state":"CA",
     "zip":"90210",
     "price":"319250"},
    {"h_id":"6",
     "city":"Dallas",
     "state":"TX",
     "zip":"75000",
     "price":"556699"},
    {"h_id":"5",
     "city":"New York",
     "state":"NY",
     "zip":"00010",
     "price":"962500"}
    ];

_.orderBy(homes, ['city', 'state', 'zip'], ['asc', 'desc', 'asc']);
Harvey
quelle
23

Hier gibt es viele gute Antworten, aber ich möchte darauf hinweisen, dass sie sehr einfach erweitert werden können, um eine viel komplexere Sortierung zu erreichen. Das einzige, was Sie tun müssen, ist, den Operator OR zu verwenden, um Vergleichsfunktionen wie folgt zu verketten:

objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )

Wo fn1,fn2 ... sind die Sortierfunktionen der Rückkehr [-1,0,1]. Dies führt zu "Sortieren nach fn1", "Sortieren nach fn2", was in SQL ziemlich gleich ORDER BY ist.

Diese Lösung basiert auf dem Verhalten des ||Operators, der den ersten ausgewerteten Ausdruck auswertet, der in true konvertiert werden kann .

Die einfachste Form hat nur eine Inline-Funktion wie diese:

// ORDER BY last_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )

Mit zwei Schritten mit last_nom, first_nomSortierreihenfolge würde wie folgt aussehen:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) || 
                  a.first_nom.localeCompare(b.first_nom)  )

Eine generische Vergleichsfunktion könnte ungefähr so ​​aussehen:

// ORDER BY <n>
let cmp = (a,b,n)=>a[n].localeCompare(b[n])

Diese Funktion kann erweitert werden, um numerische Felder, Groß- / Kleinschreibung, willkürliche Datentypen usw. zu unterstützen.

Sie können sie verwenden, um sie nach Sortierpriorität zu verketten:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> cmp(a,b, "last_nom") || cmp(a,b, "first_nom") )
// ORDER_BY last_nom, first_nom DESC
objs.sort((a,b)=> cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )
// ORDER_BY last_nom DESC, first_nom DESC
objs.sort((a,b)=> -cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )

Der Punkt hier ist, dass reines JavaScript mit funktionalem Ansatz Sie ohne externe Bibliotheken oder komplexen Code weit bringen kann. Es ist auch sehr effektiv, da kein String-Parsing durchgeführt werden muss

Tero Tolonen
quelle
23

Anwendungsbeispiel:

objs.sort(sortBy('last_nom'));

Skript:

/**
 * @description
 * Returns a function which will sort an
 * array of objects by the given key.
 *
 * @param  {String}  key
 * @param  {Boolean} reverse
 * @return {Function}
 */
const sortBy = (key, reverse) => {

  // Move smaller items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveSmaller = reverse ? 1 : -1;

  // Move larger items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveLarger = reverse ? -1 : 1;

  /**
   * @param  {*} a
   * @param  {*} b
   * @return {Number}
   */
  return (a, b) => {
    if (a[key] < b[key]) {
      return moveSmaller;
    }
    if (a[key] > b[key]) {
      return moveLarger;
    }
    return 0;
  };
};
Jamie Mason
quelle
Vielen Dank, dass Sie dies aufgeschlüsselt haben. Ich versuche zu verstehen, warum Ziffern 1, 0, -1für die Sortierreihenfolge verwendet werden. Selbst mit Ihrer obigen Erklärung, die sehr gut aussieht - ich verstehe sie immer noch nicht ganz. Ich denke immer an die -1Verwendung der Array-Längeneigenschaft, dh: arr.length = -1bedeutet, dass das Element nicht gefunden wird. Ich vermische hier wahrscheinlich die Dinge, aber können Sie mir helfen zu verstehen, warum Ziffern 1, 0, -1zur Bestimmung der Reihenfolge verwendet werden? Vielen Dank.
Chris22
1
Dies ist nicht ganz richtig, aber es könnte hilfreich sein, sich das so vorzustellen: Die an array.sort übergebene Funktion wird für jedes Element im Array einmal als Argument mit dem Namen "a" aufgerufen. Der Rückgabewert jedes Funktionsaufrufs gibt an, wie der Index (aktuelle Positionsnummer) des Elements "a" im Vergleich zum nächsten Element "b" geändert werden soll. Der Index bestimmt die Reihenfolge des Arrays (0, 1, 2 usw.). Wenn also "a" bei Index 5 ist und Sie -1 zurückgeben, dann 5 + -1 == 4 (bewegen Sie es näher an die Vorderseite) 5 + 0 == 5 (Behalte es dort, wo es ist) usw. Es geht durch das Array und vergleicht jedes Mal 2 Nachbarn, bis es das Ende erreicht, wobei ein sortiertes Array übrig bleibt.
Jamie Mason
Vielen Dank, dass Sie sich die Zeit genommen haben, dies weiter zu erklären. Anhand Ihrer Erklärung und des MDN Array.prototype.sort erkläre ich Ihnen, was ich davon halte : Im Vergleich zu aund b, wenn agrößer als b1, fügen Sie 1 zum Index von hinzu aund platzieren Sie es dahinter b, wenn akleiner als b, subtrahiere 1 von aund lege es vor b. Wenn aund gleich bsind, fügen Sie 0 hinzu aund lassen Sie es dort, wo es ist.
Chris22
22

Ich habe diesen speziellen Ansatz nicht vorgeschlagen, daher hier eine knappe Vergleichsmethode, die ich gerne verwende und die für beide funktioniert string und number:

const objs = [ 
  { first_nom: 'Lazslo', last_nom: 'Jamf'     },
  { first_nom: 'Pig',    last_nom: 'Bodine'   },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

const sortBy = fn => (a, b) => {
  const fa = fn(a);
  const fb = fn(b);
  return -(fa < fb) || +(fa > fb);
};

const getLastName = o => o.last_nom;
const sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));

Hier ist eine Erklärung von sortBy() :

sortBy() akzeptiert a fn , das auswählt, welcher Wert aus einem Objekt als Vergleich verwendet werden soll, und gibt eine Funktion zurück, an die direkt übergeben werden kannArray.prototype.sort() . In diesem Beispiel verwenden wir o.last_nomals Vergleichswert, also immer dann, wenn wir zwei Objekte über Array.prototype.sort()z

{ first_nom: 'Lazslo', last_nom: 'Jamf' }

und

{ first_nom: 'Pig', last_nom: 'Bodine' }

wir gebrauchen

(a, b) => {
  const fa = fn(a);
  const fb = fn(b);
  return -(fa < fb) || +(fa > fb);
};

um sie zu vergleichen.

Daran erinnern fn = o => o.last_nom wir uns können wir die Vergleichsfunktion auf das Äquivalent erweitern

(a, b) => {
  const fa = a.last_nom;
  const fb = b.last_nom;
  return -(fa < fb) || +(fa > fb);
};

Das logische ODER || Operator verfügt über eine Kurzschlussfunktion, die hier sehr nützlich ist. Aufgrund seiner Funktionsweise bedeutet der Hauptteil der obigen Funktion

if (fa < fb) return -1;
if (fa > fb) return 1;
return 0;

Als zusätzlichen Bonus ist hier das Äquivalent in ECMAScript 5 ohne Pfeilfunktionen, das leider ausführlicher ist:

var objs = [ 
  { first_nom: 'Lazslo', last_nom: 'Jamf'     },
  { first_nom: 'Pig',    last_nom: 'Bodine'   },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

var sortBy = function (fn) {
  return function (a, b) {
    var fa = fn(a);
    var fb = fn(b);
    return -(fa < fb) || +(fa > fb);
  };
};

var getLastName = function (o) { return o.last_nom };
var sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));

Patrick Roberts
quelle
17

Ich weiß, dass diese Frage zu alt ist, aber ich habe keine ähnliche Implementierung wie meine gesehen.
Diese Version basiert auf der Schwartzschen Transformationssprache .

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

Hier ist ein Beispiel für die Verwendung:

let games = [
  { name: 'Mashraki',          rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));
a8m
quelle
14

Sortieren (mehr) komplexer Arrays von Objekten

Da Sie wahrscheinlich auf komplexere Datenstrukturen wie dieses Array stoßen, würde ich die Lösung erweitern.

TL; DR

Sind mehr steckbare Version basierend auf @ ege-Özcans sehr schöne Antwort .

Problem

Ich bin auf das Folgende gestoßen und konnte es nicht ändern. Ich wollte das Objekt auch nicht vorübergehend abflachen. Ich wollte auch keinen Unterstrich / lodash verwenden, hauptsächlich aus Leistungsgründen und um Spaß daran zu haben, es selbst zu implementieren.

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

Tor

Das Ziel ist es, es primär nach People.Name.nameund sekundär nach zu sortierenPeople.Name.surname

Hindernisse

In der Basislösung wird nun die Klammernotation verwendet, um die Eigenschaften zu berechnen, nach denen dynamisch sortiert werden soll. Hier müssten wir jedoch die Klammer-Notation auch dynamisch konstruieren, da Sie einige davon erwarten würdenPeople['Name.name'] davon funktionieren würden - was nicht funktioniert.

Das einfache Tun People['Name']['name']ist dagegen statisch und erlaubt Ihnen nur, das n hinunterzugehen te Ebene .

Lösung

Der Hauptzusatz besteht darin, den Objektbaum entlangzugehen und den Wert des letzten Blattes, das Sie angeben müssen, sowie eines Zwischenblatts zu bestimmen.

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

People.sort(dynamicMultiSort(['Name','name'], ['Name', '-surname']));
// Results in...
// [ { Name: { name: 'AAA', surname: 'ZZZ' }, Middlename: 'Abrams' },
//   { Name: { name: 'Name', surname: 'Surname' }, Middlename: 'JJ' },
//   { Name: { name: 'Name', surname: 'AAA' }, Middlename: 'Wars' } ]

// same logic as above, but strong deviation for dynamic properties 
function dynamicSort(properties) {
  var sortOrder = 1;
  // determine sort order by checking sign of last element of array
  if(properties[properties.length - 1][0] === "-") {
    sortOrder = -1;
    // Chop off sign
    properties[properties.length - 1] = properties[properties.length - 1].substr(1);
  }
  return function (a,b) {
    propertyOfA = recurseObjProp(a, properties)
    propertyOfB = recurseObjProp(b, properties)
    var result = (propertyOfA < propertyOfB) ? -1 : (propertyOfA > propertyOfB) ? 1 : 0;
    return result * sortOrder;
  };
}

/**
 * Takes an object and recurses down the tree to a target leaf and returns it value
 * @param  {Object} root - Object to be traversed.
 * @param  {Array} leafs - Array of downwards traversal. To access the value: {parent:{ child: 'value'}} -> ['parent','child']
 * @param  {Number} index - Must not be set, since it is implicit.
 * @return {String|Number}       The property, which is to be compared by sort.
 */
function recurseObjProp(root, leafs, index) {
  index ? index : index = 0
  var upper = root
  // walk down one level
  lower = upper[leafs[index]]
  // Check if last leaf has been hit by having gone one step too far.
  // If so, return result from last step.
  if (!lower) {
    return upper
  }
  // Else: recurse!
  index++
  // HINT: Bug was here, for not explicitly returning function
  // https://stackoverflow.com/a/17528613/3580261
  return recurseObjProp(lower, leafs, index)
}

/**
 * Multi-sort your array by a set of properties
 * @param {...Array} Arrays to access values in the form of: {parent:{ child: 'value'}} -> ['parent','child']
 * @return {Number} Number - number for sort algorithm
 */
function dynamicMultiSort() {
  var args = Array.prototype.slice.call(arguments); // slight deviation to base

  return function (a, b) {
    var i = 0, result = 0, numberOfProperties = args.length;
    // REVIEW: slightly verbose; maybe no way around because of `.sort`-'s nature
    // Consider: `.forEach()`
    while(result === 0 && i < numberOfProperties) {
      result = dynamicSort(args[i])(a, b);
      i++;
    }
    return result;
  }
}

Beispiel

Arbeitsbeispiel für JSBin

eljefedelrodeodeljefe
quelle
2
Warum? Dies ist nicht die Antwort auf die ursprüngliche Frage und "das Ziel" könnte einfach mit People.sort ((a, b) => {return a.Name.name.localeCompare (b.Name.name) || a.Name gelöst werden .surname.localeCompare (b.Name.surname)})
Tero Tolonen
12

Noch eine Option:

var someArray = [...];

function generateSortFn(prop, reverse) {
    return function (a, b) {
        if (a[prop] < b[prop]) return reverse ? 1 : -1;
        if (a[prop] > b[prop]) return reverse ? -1 : 1;
        return 0;
    };
}

someArray.sort(generateSortFn('name', true));

sortiert standardmäßig aufsteigend.

Ravshan Samandarov
quelle
1
Eine leicht geänderte Version zum Sortieren nach mehreren Feldern ist bei Bedarf hier: stackoverflow.com/questions/6913512/…
Ravshan Samandarov
es sieht so aus, als könnte es die nächste sein: export function generateSortFn (prop: string, reverse: boolean = false): (... args: any) => number {return (a, b) => {return a [prop ] <b [prop]? umkehren ? 1: -1: a [Stütze]> b [Stütze]? umkehren ? -1: 1: 0; }; }
Den Kerny
Ich würde lieber lesbaren Code als den kürzesten bevorzugen.
Ravshan Samandarov
stimmte zu, aber in einigen Fällen muss ich mir die Dienstprogrammfunktionen nicht ansehen.
Den Kerny
11

Eine einfache Funktion, die ein Objektarray nach einer Eigenschaft sortiert

function sortArray(array, property, direction) {
    direction = direction || 1;
    array.sort(function compare(a, b) {
        let comparison = 0;
        if (a[property] > b[property]) {
            comparison = 1 * direction;
        } else if (a[property] < b[property]) {
            comparison = -1 * direction;
        }
        return comparison;
    });
    return array; // Chainable
}

Verwendungszweck:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

sortArray(objs, "last_nom"); // Asc
sortArray(objs, "last_nom", -1); // Desc
Francois Girard
quelle
Diese Lösung funktionierte perfekt für mich, um bidirektional zu sortieren. Vielen Dank u
manjuvreddy
10

Ein einfacher Weg:

objs.sort(function(a,b) {
  return b.last_nom.toLowerCase() < a.last_nom.toLowerCase();
});

'.toLowerCase()'Beachten Sie, dass dies erforderlich ist, um Fehler beim Vergleichen von Zeichenfolgen zu vermeiden.

Caio Ladislau
quelle
4
Sie könnten verwenden Pfeil Funktionen den Code ein wenig mehr elegant zu lassen:objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );
Sertage
Dies ist aus dem gleichen Grund wie hier erläutert falsch .
Patrick Roberts
2
Pfeilfunktionen sind nicht ES5-würdig. Tonnenweise Motoren sind immer noch auf ES5 beschränkt. In meinem Fall finde ich die Antwort oben deutlich besser, da ich auf einem ES5-Motor bin (von meiner Firma gezwungen)
Dylanh724
9

zusätzliche Desc-Parameter für Ege Özcan- Code

function dynamicSort(property, desc) {
    if (desc) {
        return function (a, b) {
            return (a[property] > b[property]) ? -1 : (a[property] < b[property]) ? 1 : 0;
        }   
    }
    return function (a, b) {
        return (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
    }
}
Behnam Shomali
quelle
9

Wenn Sie die dynamische Lösung von Ege mit der Idee von Vinay kombinieren, erhalten Sie eine schöne robuste Lösung:

Array.prototype.sortBy = function() {
    function _sortByAttr(attr) {
        var sortOrder = 1;
        if (attr[0] == "-") {
            sortOrder = -1;
            attr = attr.substr(1);
        }
        return function(a, b) {
            var result = (a[attr] < b[attr]) ? -1 : (a[attr] > b[attr]) ? 1 : 0;
            return result * sortOrder;
        }
    }
    function _getSortFunc() {
        if (arguments.length == 0) {
            throw "Zero length arguments not allowed for Array.sortBy()";
        }
        var args = arguments;
        return function(a, b) {
            for (var result = 0, i = 0; result == 0 && i < args.length; i++) {
                result = _sortByAttr(args[i])(a, b);
            }
            return result;
        }
    }
    return this.sort(_getSortFunc.apply(null, arguments));
}

Verwendungszweck:

// Utility for printing objects
Array.prototype.print = function(title) {
    console.log("************************************************************************");
    console.log("**** "+title);
    console.log("************************************************************************");
    for (var i = 0; i < this.length; i++) {
        console.log("Name: "+this[i].FirstName, this[i].LastName, "Age: "+this[i].Age);
    }
}

// Setup sample data
var arrObj = [
    {FirstName: "Zach", LastName: "Emergency", Age: 35},
    {FirstName: "Nancy", LastName: "Nurse", Age: 27},
    {FirstName: "Ethel", LastName: "Emergency", Age: 42},
    {FirstName: "Nina", LastName: "Nurse", Age: 48},
    {FirstName: "Anthony", LastName: "Emergency", Age: 44},
    {FirstName: "Nina", LastName: "Nurse", Age: 32},
    {FirstName: "Ed", LastName: "Emergency", Age: 28},
    {FirstName: "Peter", LastName: "Physician", Age: 58},
    {FirstName: "Al", LastName: "Emergency", Age: 51},
    {FirstName: "Ruth", LastName: "Registration", Age: 62},
    {FirstName: "Ed", LastName: "Emergency", Age: 38},
    {FirstName: "Tammy", LastName: "Triage", Age: 29},
    {FirstName: "Alan", LastName: "Emergency", Age: 60},
    {FirstName: "Nina", LastName: "Nurse", Age: 54}
];

//Unit Tests
arrObj.sortBy("LastName").print("LastName Ascending");
arrObj.sortBy("-LastName").print("LastName Descending");
arrObj.sortBy("LastName", "FirstName", "-Age").print("LastName Ascending, FirstName Ascending, Age Descending");
arrObj.sortBy("-FirstName", "Age").print("FirstName Descending, Age Ascending");
arrObj.sortBy("-Age").print("Age Descending");
Mike R.
quelle
1
Danke für die Idee! Bitte ermutigen Sie die Leute übrigens nicht, den Array-Prototyp zu ändern (siehe die Warnung am Ende meines Beispiels).
Ege Özcan
8

Gemäß Ihrem Beispiel müssen Sie nach zwei Feldern (Nachname, Vorname) und nicht nach einem Feld sortieren. Sie können die Alasql- Bibliothek verwenden, um diese Sortierung in einer Zeile vorzunehmen:

var res = alasql('SELECT * FROM ? ORDER BY last_nom, first_nom',[objs]);

Versuchen Sie dieses Beispiel bei jsFiddle .

Agershun
quelle
8
objs.sort(function(a,b){return b.last_nom>a.last_nom})
Roshni Bokade
quelle
1
Eigentlich schien es nicht zu funktionieren, musste die akzeptierte Antwort verwenden. Es wurde nicht richtig sortiert.
Madprops
8

Angesichts des ursprünglichen Beispiels:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

Nach mehreren Feldern sortieren:

objs.sort(function(left, right) {
    var last_nom_order = left.last_nom.localeCompare(right.last_nom);
    var first_nom_order = left.first_nom.localeCompare(right.first_nom);
    return last_nom_order || first_nom_order;
});

Anmerkungen

  • a.localeCompare(b)wird allgemein unterstützt und kehrt -1,0,1 wenn a<b, a==b,a>b bzw..
  • ||in der letzten Zeile gibt last_nomVorrang vorfirst_nom .
  • Die Subtraktion funktioniert bei numerischen Feldern: var age_order = left.age - right.age;
  • Negieren, um die Reihenfolge umzukehren, return -last_nom_order || -first_nom_order || -age_order;
Bob Stein
quelle
8

Versuche dies,

UPTO ES5

//Ascending Sort
items.sort(function (a, b) {
   return a.value - b.value;
});


//Descending Sort
items.sort(function (a, b) {
   return b.value - a.value;
});


IN ES6 & above:

// Ascending sort
items.sort((a, b) => a.value - b.value);

// Descending Sort
 items.sort((a, b) => b.value - a.value);
Abhishek
quelle
beste und einfachste Lösung
Omar Hasan
7

Möglicherweise müssen Sie sie in Kleinbuchstaben umwandeln, um Verwirrung zu vermeiden.

objs.sort(function (a,b) {

var nameA=a.last_nom.toLowerCase(), nameB=b.last_nom.toLowerCase()

if (nameA < nameB)
  return -1;
if (nameA > nameB)
  return 1;
return 0;  //no sorting

})
Burak Keceli
quelle
7
function compare(propName) {
    return function(a,b) {
        if (a[propName] < b[propName])
            return -1;
        if (a[propName] > b[propName])
            return 1;
        return 0;
    };
}

objs.sort(compare("last_nom"));
Evgenii
quelle
1
Bitte überlegen Sie, Ihren Beitrag zu bearbeiten, um weitere Erklärungen dazu hinzuzufügen, was Ihr Code tut und warum er das Problem löst. Eine Antwort, die meistens nur Code enthält (auch wenn sie funktioniert), hilft dem OP normalerweise nicht, ihr Problem zu verstehen.
Drenmi
7

Mit Ramda,

npm install ramda

import R from 'ramda'
var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];
var ascendingSortedObjs = R.sortBy(R.prop('last_nom'), objs)
var descendingSortedObjs = R.reverse(ascendingSortedObjs)
Sridhar Sg
quelle
6

Dies ist ein einfaches Problem, ich weiß nicht, warum Menschen so komplexe Lösungen haben.
Eine einfache Sortierfunktion (basierend auf dem Schnellsortieralgorithmus ):

function sortObjectsArray(objectsArray, sortKey)
        {
            // Quick Sort:
            var retVal;

            if (1 < objectsArray.length)
            {
                var pivotIndex = Math.floor((objectsArray.length - 1) / 2);  // middle index
                var pivotItem = objectsArray[pivotIndex];                    // value in the middle index
                var less = [], more = [];

                objectsArray.splice(pivotIndex, 1);                          // remove the item in the pivot position
                objectsArray.forEach(function(value, index, array)
                {
                    value[sortKey] <= pivotItem[sortKey] ?                   // compare the 'sortKey' proiperty
                        less.push(value) :
                        more.push(value) ;
                });

                retVal = sortObjectsArray(less, sortKey).concat([pivotItem], sortObjectsArray(more, sortKey));
            }
            else
            {
                retVal = objectsArray;
            }

            return retVal;
        }

Anwendungsbeispiel:

var myArr = 
        [
            { val: 'x', idx: 3 },
            { val: 'y', idx: 2 },
            { val: 'z', idx: 5 },
        ];
myArr = sortObjectsArray(myArr, 'idx');
Gil Epshtain
quelle
5
Wie ist die Implementierung der schnellen Sortierung in js eine einfache Lösung? Einfacher Algorithmus, aber keine einfache Lösung.
Andrew
Es ist einfach, da keine äußeren Bibliotheken verwendet werden und der Prototyp des Objekts nicht geändert wird. Meiner Meinung nach hat die Länge des Codes keinen direkten Einfluss auf die Komplexität des Codes
Gil Epshtain
2
Lassen Sie mich mit anderen Worten versuchen: Wie ist es eine einfache Lösung, das Rad neu zu erfinden?
Roberto14