So sortieren Sie Zeichenfolgen in JavaScript

343

Ich habe eine Liste von Objekten, die ich basierend auf einem Feld attrvom Typ Zeichenfolge sortieren möchte . Ich habe es versucht-

list.sort(function (a, b) {
    return a.attr - b.attr
})

Es wurde jedoch festgestellt, dass -dies mit Zeichenfolgen in JavaScript nicht funktioniert. Wie kann ich eine Liste von Objekten basierend auf einem Attribut mit Typzeichenfolge sortieren?

Airportyh
quelle
1
siehe JavaScript case insensitive string comparisonauf stackoverflow.com/questions/2140627/…
Adrien Be
Für eine schnelle "internationalisierte" Lösung (nur teilweise, da dies möglicherweise nicht alle Akzente der Welt abdeckt) möchten Sie möglicherweise einfach Akzente ignorieren, dh sie entfernen. Dann machen Sie nur Ihren String-Vergleich, siehe Javascript : remove accents/diacritics in stringsauf stackoverflow.com/questions/990904/…
Adrien Be
2
Lustigerweise schrieb Jeff Atwood selbst 2007 einen Blog-Beitrag über dieses häufige Problem, siehe blog.codinghorror.com/sorting-for-humans-natural-sort-order
Adrien Be

Antworten:

619

Verwenden Sie String.prototype.localeCompareein Beispiel:

list.sort(function (a, b) {
    return ('' + a.attr).localeCompare(b.attr);
})

Wir erzwingen, dass a.attr eine Zeichenfolge ist, um Ausnahmen zu vermeiden. localeComparewird seit Internet Explorer 6 und Firefox 1 unterstützt. Möglicherweise wird auch der folgende verwendete Code angezeigt, der ein Gebietsschema nicht berücksichtigt:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;
Shog9
quelle
81
Bevor jemand den gleichen hastigen Fehler macht wie ich, ist es local e Compare, nicht localCompare.
Ento
12
Bei der ersten Lösung wird "A" nach "z", aber vor "Z" betrachtet, da ein Vergleich des Zeichen-ASCII-Werts durchgeführt wird. localeCompare()stößt nicht auf dieses Problem, versteht aber keine Zahlen, sodass Sie ["1", "10", "2"] erhalten, wie beim Sortieren von Vergleichen in den meisten Sprachen. Wenn Sie für Ihr UI-Frontend sortieren möchten, schauen Sie sich den Algorithmus alphanum / natural sort an. stackoverflow.com/questions/4340227/… oder stackoverflow.com/questions/4321829/…
Dead.Rabit
2
Beachten Sie, dass dies localeCompare()nur in modernen Browsern unterstützt wird: IE11 + zum Zeitpunkt des Schreibens, siehe developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Adrien Be
3
Nein, ich meine die erste Zeile der Tabelle, @Adrien - IE unterstützt localeCompare()das Zurücksetzen vieler Versionen, unterstützt jedoch nicht die Angabe des Gebietsschemas bis Version 11. Beachten Sie auch die Fragen, mit denen Dead.Rabit verknüpft ist.
Shog9
3
@ Shog9 mein schlechtes, es scheint, als ob es seit IE6 unterstützt wird! Siehe (Bildlauf nach unten / Suche zur Methode localeCompare ()) unter msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx . Beachten Sie jedoch, dass in den alten Implementierungen, in denen die Argumente für Gebietsschemas und Optionen (die vor IE11 verwendet wurden) nicht verwendet werden, das Gebietsschema und die Sortierreihenfolge vollständig implementierungsabhängig sind , mit anderen Worten: Firefox, Safari, Chrome und IE Sortieren Sie die Zeichenfolgen NICHT in derselben Reihenfolge. siehe code.google.com/p/v8/issues/detail?id=459
Adrien Be
165

Eine aktualisierte Antwort (Oktober 2014)

Ich war wirklich verärgert über diese natürliche Sortierreihenfolge, daher habe ich einige Zeit gebraucht, um dieses Problem zu untersuchen. Ich hoffe das hilft.

Um es kurz zu machen

localeCompare()Charakterunterstützung ist schlecht, benutze sie einfach. Wie bereits erwähnt, Shog9lautet die Antwort auf Ihre Frage:

return item1.attr.localeCompare(item2.attr);

Fehler, die in allen benutzerdefinierten Javascript-Implementierungen "Sortierung nach natürlicher Zeichenfolge" gefunden wurden

Es gibt eine ganze Reihe von benutzerdefinierten Implementierungen, die versuchen, einen String-Vergleich genauer durchzuführen, der als "natürliche String-Sortierreihenfolge" bezeichnet wird.

Beim "Spielen" mit diesen Implementierungen fiel mir immer eine seltsame "natürliche Sortierreihenfolge" auf, oder eher Fehler (oder Auslassungen im besten Fall).

In der Regel werden Sonderzeichen (Leerzeichen, Bindestrich, kaufmännisches Und, Klammern usw.) nicht korrekt verarbeitet.

Sie werden dann feststellen, dass sie an verschiedenen Stellen gemischt erscheinen. Dies kann normalerweise sein:

  • Einige werden zwischen dem Großbuchstaben 'Z' und dem Kleinbuchstaben 'a' stehen.
  • Einige werden zwischen der '9' und dem Großbuchstaben 'A' stehen.
  • einige werden nach Kleinbuchstaben 'z' stehen

Wenn man erwartet hätte, dass alle Sonderzeichen an einem Ort "gruppiert" werden, außer vielleicht dem Leerzeichen-Sonderzeichen (das immer das erste Zeichen wäre). Das heißt, entweder alle vor Zahlen oder alle zwischen Zahlen und Buchstaben (Klein- und Großbuchstaben werden nacheinander "zusammen") oder alle nach Buchstaben.

Mein Fazit ist, dass sie alle keine konsistente Reihenfolge liefern, wenn ich anfange, kaum ungewöhnliche Zeichen hinzuzufügen (dh Zeichen mit diakritischen Zeichen oder Zeichen wie Bindestrich, Ausrufezeichen usw.).

Recherche zu benutzerdefinierten Implementierungen:

Browsers native "Natural String Sort Order" -Implementierungen über localeCompare()

localeCompare()Die älteste Implementierung (ohne die Argumente für Gebietsschemas und Optionen) wird von IE6 + unterstützt (siehe http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (scrollen Sie nach unten zu localeCompare ()). ) Methode). Die integrierte localeCompare()Methode eignet sich viel besser zum Sortieren, auch für internationale und Sonderzeichen. Das einzige Problem bei der Verwendung der localeCompare()Methode besteht darin, dass "das verwendete Gebietsschema und die verwendete Sortierreihenfolge vollständig von der Implementierung abhängen". Mit anderen Worten, wenn Sie localeCompare wie stringOne.localeCompare (stringTwo) verwenden: Firefox, Safari, Chrome und IE haben eine andere Sortierreihenfolge für Strings.

Recherche zu den browser-nativen Implementierungen:

Schwierigkeit der "natürlichen Sortierreihenfolge der Zeichenfolge"

Die Implementierung eines soliden Algorithmus (dh konsistent, aber auch für eine Vielzahl von Zeichen) ist eine sehr schwierige Aufgabe. UTF8 enthält mehr als 2000 Zeichen und umfasst mehr als 120 Skripte (Sprachen) . Schließlich gibt es einige Spezifikationen für diese Aufgaben, die als "Unicode-Kollatierungsalgorithmus" bezeichnet werden und unter http://www.unicode.org/reports/tr10/ zu finden sind . Weitere Informationen hierzu finden Sie zu dieser Frage, die ich unter /software/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order veröffentlicht habe

Schlußfolgerung

In Anbetracht des aktuellen Unterstützungsniveaus der benutzerdefinierten Javascript-Implementierungen, auf die ich gestoßen bin, werden wir wahrscheinlich nie sehen, dass irgendetwas der Unterstützung all dieser Zeichen und Skripte (Sprachen) nahe kommt. Daher würde ich lieber die native localeCompare () -Methode des Browsers verwenden. Ja, es hat den Nachteil, dass es in allen Browsern nicht konsistent ist, aber grundlegende Tests zeigen, dass es einen viel größeren Bereich von Zeichen abdeckt und solide und aussagekräftige Sortierreihenfolgen ermöglicht.

Wie bereits erwähnt, Shog9lautet die Antwort auf Ihre Frage:

return item1.attr.localeCompare(item2.attr);

Weiterführende Literatur:

Dank der netten Antwort von Shog9, die mich in die "richtige" Richtung gebracht hat, glaube ich

Adrien Be
quelle
38

Antwort (in Modern ECMAScript)

list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))

Oder

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))

Beschreibung

Das Umwandeln eines booleschen Werts in eine Zahl ergibt Folgendes:

  • true -> 1
  • false -> 0

Betrachten Sie drei mögliche Muster:

  • x ist größer als y: (x > y) - (y < x)-> 1 - 0->1
  • x ist gleich y: (x > y) - (y < x)-> 0 - 0->0
  • x ist kleiner als y: (x > y) - (y < x)-> 0 - 1->-1

(Alternative)

  • x ist größer als y: +(x > y) || -(x < y)-> 1 || 0->1
  • x ist gleich y: +(x > y) || -(x < y)-> 0 || 0->0
  • x ist kleiner als y: +(x > y) || -(x < y)-> 0 || -1->-1

Diese Logik entspricht also typischen Sortierkomparatorfunktionen.

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;
mpyw
quelle
1
Wie ich bereits zu der früheren Antwort mit diesem Trick gesagt habe, können Nur-Code-Antworten nützlicher gemacht werden, indem erklärt wird, wie sie funktionieren.
Dan Dascalescu
Beschreibung hinzugefügt
mpyw
Können Sie kommentieren, ob dies besser oder schlechter als localeCompare ist?
Ran Lottem
3
@RanLottem localeCompareund Standardvergleich liefern unterschiedliche Ergebnisse. Was erwartest du? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b))sortiert in alphabetischer Reihenfolge ohne ["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b))
Berücksichtigung der
Ich sehe, das scheint ein Hauptknackpunkt zu sein. Irgendeine Idee zu Leistungsunterschieden?
Ran Lottem
13

Sie sollten hier> oder <und == verwenden. Die Lösung wäre also:

list.sort(function(item1, item2) {
    var val1 = item1.attr,
        val2 = item2.attr;
    if (val1 == val2) return 0;
    if (val1 > val2) return 1;
    if (val1 < val2) return -1;
});
Airportyh
quelle
1
Nebenbei bemerkt, dies behandelt keine Vergleiche zwischen Zeichenfolgen und Zahlen. Zum Beispiel: 'Z' <9 (falsch), 'Z'> 9 (auch falsch ??), 'Z' == 9 (auch falsch !!). Dumme NaN in JavaScript ...
Kato
7

Verschachtelte ternäre Pfeilfunktion

(a,b) => (a < b ? -1 : a > b ? 1 : 0)
Geckos
quelle
7

Da Zeichenfolgen direkt in Javascript verglichen werden können, ist dies der Fall

list.sort(function (a, b) {
    return a.attr > b.attr ? 1: -1;
})

Die Subtraktion in einer Sortierfunktion wird nur verwendet, wenn eine nicht alphabetische (numerische) Sortierung gewünscht wird und natürlich nicht mit Zeichenfolgen funktioniert

Alejadro Xalabarder
quelle
6

Ich hatte mich lange darum gekümmert, also habe ich es endlich recherchiert und dir diesen langwierigen Grund gegeben, warum die Dinge so sind, wie sie sind.

Aus der Spezifikation :

Section 11.9.4   The Strict Equals Operator ( === )

The production EqualityExpression : EqualityExpression === RelationalExpression
is evaluated as follows: 
- Let lref be the result of evaluating EqualityExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating RelationalExpression.
- Let rval be GetValue(rref).
- Return the result of performing the strict equality comparison 
  rval === lval. (See 11.9.6)

Nun gehen wir zu 11.9.6

11.9.6   The Strict Equality Comparison Algorithm

The comparison x === y, where x and y are values, produces true or false. 
Such a comparison is performed as follows: 
- If Type(x) is different from Type(y), return false.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is Number, then
...
- If Type(x) is String, then return true if x and y are exactly the 
  same sequence of characters (same length and same characters in 
  corresponding positions); otherwise, return false.

Das ist es. Der Operator "Triple Equals", der auf Zeichenfolgen angewendet wird, gibt "true" zurück, wenn die Argumente genau dieselben Zeichenfolgen sind (gleiche Länge und gleiche Zeichen an entsprechenden Positionen).

Dies ===funktioniert auch in den Fällen, in denen wir versuchen, Zeichenfolgen zu vergleichen, die möglicherweise aus verschiedenen Quellen stammen, von denen wir jedoch wissen, dass sie letztendlich dieselben Werte haben - ein Szenario, das für Inline-Zeichenfolgen in unserem Code häufig genug ist. Wenn wir beispielsweise eine Variable mit dem Namen connection_statehaben und wissen möchten, in welchem ​​der folgenden Zustände ['connecting', 'connected', 'disconnecting', 'disconnected']sie sich gerade befindet, können wir die direkt verwenden=== .

Aber es gibt noch mehr. Knapp über 11.9.4 gibt es einen kurzen Hinweis:

NOTE 4     
  Comparison of Strings uses a simple equality test on sequences of code 
  unit values. There is no attempt to use the more complex, semantically oriented
  definitions of character or string equality and collating order defined in the 
  Unicode specification. Therefore Strings values that are canonically equal
  according to the Unicode standard could test as unequal. In effect this 
  algorithm assumes that both Strings are already in normalized form.

Hmm. Was jetzt? Extern erhaltene Saiten können und werden höchstwahrscheinlich eine seltsame Unicodey sein, und unsere Sanftheit ===wird ihnen nicht gerecht. In kommt localeComparezur Rettung:

15.5.4.9   String.prototype.localeCompare (that)
    ...
    The actual return values are implementation-defined to permit implementers 
    to encode additional information in the value, but the function is required 
    to define a total ordering on all Strings and to return 0 when comparing
    Strings that are considered canonically equivalent by the Unicode standard. 

Wir können jetzt nach Hause gehen.

tl; dr;

Um Strings in Javascript, verwenden Sie zu vergleichen localeCompare; Wenn Sie wissen, dass die Zeichenfolgen keine Nicht-ASCII-Komponenten enthalten, da es sich beispielsweise um interne Programmkonstanten handelt, ===funktioniert dies ebenfalls.

Manav
quelle
0

In Ihrer Operation in Ihrer ersten Frage führen Sie die folgende Operation aus:

item1.attr - item2.attr

Angenommen, es handelt sich um Zahlen (dh item1.attr = "1", item2.attr = "2"). Sie können weiterhin den Operator "===" (oder andere strenge Bewerter) verwenden, sofern Sie den Typ sicherstellen. Folgendes sollte funktionieren:

return parseInt(item1.attr) - parseInt(item2.attr);

Wenn sie alphaNumerisch sind, verwenden Sie localCompare ().

Eierwaren
quelle
0
list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

Wie sie arbeiten Proben:

+('aaa'>'bbb')||+('aaa'==='bbb')-1
+(false)||+(false)-1
0||0-1
-1

+('bbb'>'aaa')||+('bbb'==='aaa')-1
+(true)||+(false)-1
1||0-1
1

+('aaa'>'aaa')||+('aaa'==='aaa')-1
+(false)||+(true)-1
0||1-1
0
Petr Varyagin
quelle
3
Nur-Code-Antworten können nützlicher gemacht werden, indem erklärt wird, wie sie funktionieren.
Dan Dascalescu
-2
<!doctype html>
<html>
<body>
<p id = "myString">zyxtspqnmdba</p>
<p id = "orderedString"></p>
<script>
var myString = document.getElementById("myString").innerHTML;
orderString(myString);
function orderString(str) {
    var i = 0;
    var myArray = str.split("");
    while (i < str.length){
        var j = i + 1;
        while (j < str.length) {
            if (myArray[j] < myArray[i]){
                var temp = myArray[i];
                myArray[i] = myArray[j];
                myArray[j] = temp;
            }
            j++;
        }
        i++;
    }
    var newString = myArray.join("");
    document.getElementById("orderedString").innerHTML = newString;
}
</script>
</body>
</html>
Julio Munoz
quelle
1
Bitte fügen Sie Ihrer Antwort einige Informationen hinzu, wie dies die Frage lösen wird. Nur-Code-Antworten werden nicht begrüßt. Vielen Dank.
WayneOS
Hier möchten Sie die Zeichen innerhalb einer Zeichenfolge ordnen, was nicht gefragt wird. Sie können diese Sortierung einfach mit "Array.sort" erreichen, z. B. str.split (""). Sort () .join ("")
Alejadro Xalabarder
-2
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)
Abdul
quelle
Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, und geben Sie an, welche Einschränkungen und Annahmen gelten.
Dave