Jak sortować łańcuchy w JavaScript

Mam listę obiektów, które chcę sortować na podstawie pola attr typu string. Próbowałem użyć -

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

Ale okazało się, że - nie działa z łańcuchami w JavaScript. Jak mogę sortować listę obiektów na podstawie atrybutu z tekstem typu?

Author: royhowie, 2008-09-09

8 answers

Użycie String.prototype.localeCompare a na twój przykład:

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

Wymuszamy, aby. attr było ciągiem znaków, aby uniknąć WYJĄTKÓW. localeCompare jest obsługiwana od wersji Internet Explorer 6 i Firefox 1. Możesz również zobaczyć poniższy kod, który nie respektuje locale:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;
 392
Author: Shog9,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-28 22:35:45

[13]} zaktualizowana odpowiedź (Październik 2014)

Byłem naprawdę zirytowany tą naturalną kolejnością sortowania łańcuchów, więc poświęciłem trochę czasu na zbadanie tego problemu. Mam nadzieję, że to pomoże.

Long story short

localeCompare() wsparcie postaci jest hardkorowe, wystarczy go użyć. Jak zauważył Shog9, odpowiedź na twoje pytanie brzmi:

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

Błędy znalezione we wszystkich niestandardowych implementacjach javascript "natural string sort order"

Jest sporo zwyczajów implementacje tam, starając się zrobić porównanie łańcuchów dokładniej o nazwie "natural string sort order"

Kiedy "bawiłem się" tymi implementacjami, zawsze zauważyłem jakiś dziwny wybór "naturalnego porządku sortowania", a raczej błędy (lub pominięcia w najlepszych przypadkach).

Zazwyczaj znaki specjalne (spacja, myślnik, ampersand, nawiasy itd.) nie są przetwarzane poprawnie.

Znajdziesz je pojawiające się w różnych miejscach, zazwyczaj, że może be:

  • niektóre będą między wielką literą " Z "i małą literą" a "
  • niektóre będą między " 9 "A wielką literą" A "
  • niektóre będą po małych literach ' z '

Kiedy można by oczekiwać, że znaki specjalne będą "zgrupowane" razem w jednym miejscu, z wyjątkiem specjalnego znaku spacji (który zawsze byłby pierwszym znakiem). Oznacza to, że albo wszystkie przed cyframi, albo wszystkie między cyframi i literami (małe i wielkie litery są "razem" jeden po drugim), lub wszystkie po literach.

Mój wniosek jest taki, że wszystkie nie zapewniają spójnej kolejności, gdy zaczynam dodawać ledwie nietypowe znaki (tj. znaki diakrytyczne lub znaki, takie jak myślnik, wykrzyknik i tak dalej).

Badania nad niestandardowymi implementacjami:

Natywne implementacje "natural string sort order" via localeCompare()

localeCompare() najstarsza implementacja (bez argumentów locales i options) jest obsługiwana przez IE6+, zobacz http://msdn.microsoft.com/en-us/library/ie/s4esdbwz (v=vs.94). aspx (przewiń w dół do metody localeCompare ()). Wbudowana Metoda localeCompare() sprawdza się znacznie lepiej w sortowaniu, nawet znaków międzynarodowych i specjalnych. Jedynym problemem przy użyciu metody localeCompare() jest to, że "użyte ustawienia regionalne i kolejność sortowania są całkowicie zależne od implementacji". Innymi słowy, przy użyciu localeCompare takie jak stringOne.porównaj (stringTwo): Firefox, Safari , Chrome i IE mają inną kolejność sortowania dla ciągów.

Badania nad implementacjami natywnymi dla przeglądarki:

Trudność "ciągu naturalnego porządku sortowania"

Implementacja solidnego algorytmu (czyli: spójnego, ale również obejmujące szeroki zakres znaków) jest bardzo trudne zadanie. UTF8 zawiera ponad 2000 znaków & obejmuje ponad 120 skryptów (języków) . Na koniec, istnieją pewne specyfikacje dla tego zadania, nazywa się to "algorytmem Unicode Collation", który można znaleźć na http://www.unicode.org/reports/tr10 / . Więcej informacji na ten temat znajdziecie na zamieszczonym przeze mnie pytaniu https://softwareengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

Final conclusion

Biorąc pod uwagę obecny poziom wsparcia zapewnianego przez niestandardowe implementacje javascript, na które natknąłem się, prawdopodobnie nigdy nie zobaczymy niczego zbliżającego się do obsługi tych wszystkich znaków i skryptów (języków). Dlatego wolałbym użyć natywnej metody localeCompare () w przeglądarkach. Tak, to ma tę zaletę, że nie jest spójny w przeglądarkach, ale podstawowe testy pokazują, że obejmuje znacznie szerszy zakres znaków, umożliwiając solidne i znaczące porządki sortowania.

Tak jak wskazuje Shog9, odpowiedź na twoje pytanie brzmi:

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

Czytaj dalej:

Dzięki miłej odpowiedzi Shog9, która postawiła mnie w "prawo" kierunek wierzę

 115
Author: Adrien Be,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:07

Najprostsza odpowiedź z ECMAScript 2016

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

Lub

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))
 12
Author: mpyw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-07 03:59:03

Należy użyć > lub

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;
});
 7
Author: airportyh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-03 18:04:21

Długo się tym przejmowałem, więc w końcu to zbadałem i podałem ci ten długi powód, dlaczego rzeczy są takie, jakie są.

Z spec :

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)

Więc teraz przechodzimy do 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.

To jest to. operator Triple equals zastosowany do łańcuchów zwraca true iff argumenty są dokładnie tymi samymi łańcuchami (ta sama długość i te same znaki w odpowiednich pozycjach).

Więc === będzie działać w przypadkach, gdy próbujemy porównać ciągi, które mogły pochodzić z różnych źródeł, ale które, jak wiemy, będą miały te same wartości - jest to dość powszechny scenariusz dla ciągów wbudowanych w naszym kodzie. Na przykład, jeśli mamy zmienną o nazwie connection_state i chcemy wiedzieć, w którym z następujących stanów ['connecting', 'connected', 'disconnecting', 'disconnected'] jest teraz, możemy bezpośrednio użyć ===.

Ale to nie wszystko. Poniżej 11.9.4 znajduje się krótka notka:
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. Co teraz? Zewnętrznie uzyskane struny mogą, i najprawdopodobniej będzie to dziwne, a nasz łagodny === nie wymierzy im sprawiedliwości. W przychodzi localeCompare na ratunek:

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. 
Możemy już iść do domu.

Tl; dr;

Aby porównać łańcuchy w javascript, użyj localeCompare; jeśli wiesz, że łańcuchy nie mają komponentów innych niż ASCII, ponieważ są na przykład wewnętrznymi stałymi programu, to === również działa.

 4
Author: Manav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 18:06:20
<!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>
 1
Author: Julio Munoz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-23 20:45:28

W twojej operacji w Twoim początkowym pytaniu wykonujesz następującą operację:

item1.attr - item2.attr

Zakładając więc, że są to liczby (np. item1.attr = "1", item2.attr = "2") nadal możesz używać operatora "= = = " (lub innych ścisłych ewaluatorów) pod warunkiem, że zapewnisz Typ. Powinno działać:

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

Jeśli są alfanumeryczne, użyj localcompare ().

 0
Author: eggmatters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-24 18:15:33
list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

Jak działają próbki:

+('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
 0
Author: Petr Varyagin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-07 11:43:34