Как сортировать строки в JavaScript


345

У меня есть список объектов, которые я хочу отсортировать на основе поля attrтипа строки. Я пытался с помощью-

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

но обнаружил, что -не похоже на работу со строками в JavaScript. Как я могу отсортировать список объектов на основе атрибута с типом строки?


1
см. JavaScript case insensitive string comparisonна stackoverflow.com/questions/2140627/…
Adrien Be

Для быстрого «интернационализированного» решения (я полагаю, только отчасти, поскольку это может охватывать не все акценты в мире), вы можете просто игнорировать акценты, то есть удалять их. Тогда только проведите сравнение строк, см. Javascript : remove accents/diacritics in stringsНа stackoverflow.com/questions/990904/…
Adrien Be

Ответы:


622

Используйте String.prototype.localeCompareсогласно вашему примеру:

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

Мы заставляем a.attr быть строкой, чтобы избежать исключений. localeCompareподдерживается с Internet Explorer 6 и Firefox 1. Вы также можете увидеть следующий код, который не соответствует локали:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;

81
Прежде чем кто делает ту же ошибку поспешной , как я сделал, это локальный адрес Сравнит, не localCompare.
енто

12
Первое решение будет рассматривать «A» после «z», но перед «Z», так как оно сравнивает значение символа ASCII. localeCompare()не сталкивается с этой проблемой, но не понимает числовых значений, поэтому вы получите ["1", "10", "2"], как при сортировке сравнений в большинстве языков. если вы хотите выполнить сортировку для своего интерфейса пользовательского интерфейса, посмотрите алгоритм алфавитно-естественной сортировки stackoverflow.com/questions/4340227/… или stackoverflow.com/questions/4321829/…
Dead.Rabit

2
Обратите внимание , что localeCompare()поддерживается только в современных браузерах: IE11 + в момент написания, см developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Adrien Be

3
Нет, я имею в виду первую строку таблицы, @Adrien - IE поддерживает возврат localeCompare()многих версий, но не поддерживает указание локали до версии 11. Обратите внимание также на вопросы, связанные с Dead.Rabit.
Shog9

3
@ Shog9 мой плохой, похоже, что он поддерживается с IE6! см. (прокрутка вниз / поиск по методу localeCompare ()) на msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx . Следует отметить одну вещь: в старых реализациях, где мы не используем аргументы locales и options (те, что использовались до IE11), используемые локаль и порядок сортировки полностью зависят от реализации , другими словами: Firefox, Safari, Chrome & IE do НЕ сортировать строки в том же порядке. см. code.google.com/p/v8/issues/detail?id=459
Adrien Be

166

Обновленный ответ (октябрь 2014 г.)

Я был действительно раздражен этим естественным порядком сортировки строк, поэтому я потратил некоторое время на изучение этой проблемы. Надеюсь, это поможет.

Короче говоря

localeCompare()поддержка персонажей крутая, просто используйте ее. Как указано Shog9, ответ на ваш вопрос:

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

Обнаружены ошибки во всех пользовательских реализациях "естественного порядка сортировки строк" javascript

Существует довольно много пользовательских реализаций, пытающихся сделать сравнение строк, более точно названное «естественным порядком сортировки строк».

«Играя» с этими реализациями, я всегда замечал какой-то странный выбор «естественного порядка сортировки» или, скорее, ошибки (или упущения в лучших случаях).

Как правило, специальные символы (пробел, тире, амперсанд, скобки и т. Д.) Обрабатываются неправильно.

Затем вы обнаружите, что они выглядят смешанными в разных местах, обычно это может быть:

  • некоторые будут между прописными буквами «Z» и строчными буквами «а»
  • некоторые будут между '9' и прописными буквами 'A'
  • некоторые будут после строчной буквы 'z'

Когда можно было бы ожидать, что все специальные символы будут «сгруппированы» в одном месте, за исключением, возможно, специального символа пробела (который всегда будет первым символом). То есть либо все перед цифрами, либо все между цифрами и буквами (строчные и прописные буквы «вместе» один за другим) или все после букв.

Мой вывод заключается в том, что все они не обеспечивают последовательный порядок, когда я начинаю добавлять едва необычные символы (например, символы с диакритическими знаками или символами, такими как тире, восклицательный знак и т. Д.).

Исследование пользовательских реализаций:

Реализация в браузере "естественного порядка сортировки строк" через localeCompare()

localeCompare()Самая старая реализация (без аргументов locales и options) поддерживается IE6 +, см. http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (прокрутите вниз до localeCompare ( ) метод). Встроенный localeCompare()метод намного лучше справляется с сортировкой, даже международных и специальных символов. Единственная проблема при использовании localeCompare()метода заключается в том, что «используемый языковой стандарт и порядок сортировки полностью зависят от реализации». Другими словами, при использовании localeCompare, например stringOne.localeCompare (stringTwo): Firefox, Safari, Chrome & IE имеют другой порядок сортировки для строк.

Исследование нативных реализаций браузера:

Сложность «порядка естественной сортировки строк»

Реализация надежного алгоритма (то есть согласованного, но охватывающего широкий диапазон символов) является очень сложной задачей. UTF8 содержит более 2000 символов и охватывает более 120 сценариев (языков) . Наконец, есть некоторая спецификация для этих задач, она называется «Алгоритм сопоставления Unicode», которую можно найти по адресу http://www.unicode.org/reports/tr10/ . Вы можете найти больше информации по этому вопросу, который я разместил /software/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

Окончательный вывод

Поэтому, учитывая текущий уровень поддержки, предоставляемый пользовательскими реализациями javascript, с которыми я сталкивался, мы, вероятно, никогда не увидим ничего похожего на поддержку всех этих символов и сценариев (языков). Следовательно, я бы предпочел использовать родной метод браузера localeCompare (). Да, у него есть и обратная сторона: он непоследователен в разных браузерах, но базовое тестирование показывает, что он охватывает гораздо более широкий диапазон символов, что позволяет получить четкие и содержательные порядки сортировки.

Таким образом, как указано Shog9, ответ на ваш вопрос:

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

Дальнейшее чтение:

Благодаря хорошему ответу Shog9, который, как мне кажется, поставил меня в «правильное» направление


39

Ответ (в современном ECMAScript)

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

Или

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

Описание

Преобразование логического значения в число приводит к следующему:

  • true -> 1
  • false -> 0

Рассмотрим три возможных шаблона:

  • х больше чем у: (x > y) - (y < x)-> 1 - 0->1
  • х равен у: (x > y) - (y < x)-> 0 - 0->0
  • х меньше чем у: (x > y) - (y < x)-> 0 - 1->-1

(Альтернативный)

  • х больше чем у: +(x > y) || -(x < y)-> 1 || 0->1
  • х равен у: +(x > y) || -(x < y)-> 0 || 0->0
  • х меньше чем у: +(x > y) || -(x < y)-> 0 || -1->-1

Так что эти логики эквивалентны типичным функциям компаратора сортировки.

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;

1
Как я прокомментировал в предыдущем ответе, в котором использовался этот трюк, ответы только на код можно сделать более полезными, объяснив, как они работают.
Дан Даскалеску

Добавлено описание
mpyw

Можете ли вы прокомментировать, лучше это или хуже, чем localeCompare?
Побежал Лотем

3
@RanLottem localeCompareи стандартное сравнение дают разные результаты. Что вы ожидаете? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b))сортирует в алфавитном порядке без ["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b))учета регистра, в то время как в порядке кодирования
учета

Я вижу, это кажется основным камнем преткновения. Есть идеи о различиях в производительности?
Побежал Лотем

13

Вы должны использовать> или <и == здесь. Таким образом, решение будет:

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

1
С другой стороны, это не будет обрабатывать сравнение строк и чисел. Например: «Z» <9 (ложь), «Z»> 9 (тоже ложь ??), «Z» == 9 (тоже ложь !!). Глупый NaN в JavaScript ...
Като


7

поскольку строки можно сравнивать непосредственно в JavaScript, это сделает работу

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

вычитание в функции сортировки используется только тогда, когда требуется не алфавитная (числовая) сортировка и, конечно, она не работает со строками


6

Я долго беспокоился об этом, поэтому я наконец исследовал это и дал вам эту длинную причудливую причину того, почему вещи такие, какие они есть.

Из спецификации :

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)

Итак, теперь мы переходим к 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.

Вот и все. Оператор тройного равенства, применяемый к строкам, возвращает истину, если аргументы являются абсолютно одинаковыми строками (одинаковой длины и одинаковых символов в соответствующих позициях).

То ===же самое будет работать в тех случаях, когда мы пытаемся сравнить строки, которые, возможно, поступили из разных источников, но которые мы знаем, в конечном итоге будут иметь одинаковые значения - достаточно распространенный сценарий для встроенных строк в нашем коде. Например, если у нас есть переменная с именем connection_state, и мы хотим знать, в каком из следующих состояний ['connecting', 'connected', 'disconnecting', 'disconnected']она находится сейчас, мы можем напрямую использовать ===.

Но это еще не все. Чуть выше 11.9.4, есть короткое примечание:

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.

Хм. Что теперь? Струны, полученные извне, могут быть, и, скорее всего, будут странными юникодами, и наши нежные ===не сделают им должного . На localeCompareпомощь приходит :

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. 

Теперь мы можем идти домой.

ТЛ; др;

Чтобы сравнить строки в JavaScript, используйте localeCompare; если вы знаете, что в строках нет компонентов, отличных от ASCII, потому что они, например, являются внутренними программными константами, то это ===тоже работает.


0

В вашей операции в вашем первоначальном вопросе вы выполняете следующую операцию:

item1.attr - item2.attr

Таким образом, предполагая, что это числа (т.е. item1.attr = "1", item2.attr = "2"), вы все равно можете использовать оператор "===" (или другие строгие оценщики) при условии, что вы гарантируете тип. Следующее должно работать:

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

Если они буквенно-цифровые, то используйте localCompare ().


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

Как работают образцы:

+('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

3
Ответы только на код можно сделать более полезными, объяснив, как они работают.
Дан Даскалеску

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

1
Пожалуйста, добавьте некоторую информацию о том, как это решит вопрос, к вашему ответу. Ответы только на код не приветствуются. Спасибо.
wayneOS

здесь вы хотите упорядочить символы внутри строки, а это не то, что требуется. Вы можете выполнить эту сортировку, просто используя "Array.sort", например, 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)

Хотя этот код может решить вопрос, в том числе объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, получит больше голосов "за". Помните, что вы отвечаете на вопрос для читателей в будущем, а не только для того, кто спрашивает сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснение и указать, какие ограничения и предположения применяются.
Дейв
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.