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

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

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

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


person airportyh    schedule 09.09.2008    source источник


Ответы (14)


Используйте 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;
person Shog9    schedule 09.09.2008
comment
Прежде чем кто-либо совершит такую ​​же поспешную ошибку, как я, это локальное e Compare, а не localCompare. - person ento; 09.09.2012
comment
Первое решение будет считать, что A идет после z, но до Z, поскольку оно выполняет сравнение значения символа ASCII. localeCompare() не сталкивается с этой проблемой, но не понимает числовых значений, поэтому вы получите [1, 10, 2], как и при сортировке сравнений на большинстве языков. если вам нужна сортировка для пользовательского интерфейса, изучите алгоритм алфавитной / естественной сортировки stackoverflow.com/questions/4340227/ или stackoverflow.com/questions/4321829/ - person Dead.Rabit; 20.06.2013
comment
Обратите внимание, что localeCompare() поддерживается только в современных браузерах: IE11 + на момент написания, см. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ - person Adrien Be; 26.09.2014
comment
Можно заметить, что этот вопрос касается Natural Sort Algorithm .... см. stackoverflow.com/questions/2802341/ - person Adrien Be; 26.09.2014
comment
Нет, я имею в виду первую строку таблицы, @Adrien - IE поддерживает localeCompare() возврат многих версий, но не поддерживает указание языкового стандарта до версии 11. Обратите внимание также на вопросы, с которыми связался Dead.Rabit. . - person Shog9; 26.09.2014
comment
@ Shog9 мой плохой, похоже он поддерживается начиная с IE6! см. (прокрутка вниз / поиск метода localeCompare ()) на msdn.microsoft.com/en-us/library/ie/s4esdbwz (v = vs.94) .aspx. Однако следует отметить одну вещь: в старых реализациях, где мы не использовали аргументы локалей и параметров (тот, который использовался до IE11) используемые языковой стандарт и порядок сортировки полностью зависят от реализации, другими словами: Firefox , Safari, Chrome и IE НЕ сортируют строки в одном и том же порядке. см. code.google.com/p/v8/issues/detail?id = 459 - person Adrien Be; 29.09.2014
comment
@ Shog9 еще раз спасибо за вашу помощь, после некоторых эпических исследований я добавил свой ответ, так как думал, что это может понравиться сообществу, см. Ниже. - person Adrien Be; 10.10.2014
comment
См. Мой ответ о том, как localeCompare() ведет себя с разными языковыми настройками. stackoverflow.com/a/59443357/1148030 - person Peter Lamberg; 22.12.2019

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

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

Короче

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

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

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

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

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

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

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

  • некоторые будут между заглавными буквами Z и строчными буквами a.
  • некоторые будут между "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/. Дополнительную информацию об этом можно найти в опубликованном мной вопросе https://softwareengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

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

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

Итак, как указал Shog9, ответ на ваш вопрос:

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

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

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

person Adrien Be    schedule 10.10.2014

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

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

Or

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

Описание

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

  • true -> 1
  • false -> 0

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

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

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

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

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

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;
person mpyw    schedule 01.11.2016
comment
Как я прокомментировал более ранний ответ, в котором использовался этот трюк, ответы только на код можно сделать более полезными, объяснив, как они работают. . - person Dan Dascalescu; 30.10.2018
comment
Добавлено описание - person mpyw; 04.11.2018
comment
Можете ли вы прокомментировать, лучше это или хуже, чем localeCompare? - person Ran Lottem; 13.01.2019
comment
@RanLottem localeCompare и стандартное сравнение дают разные результаты. Чего вы ожидаете? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b)) сортирует в алфавитном порядке без учета регистра, а ["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b)) - в порядке кодовых точек - person mpyw; 13.01.2019
comment
Я понимаю, что это, кажется, главный камень преткновения. Есть идеи о различиях в производительности? - person Ran Lottem; 13.01.2019
comment
Я предполагаю, что порядок кодовых точек выполняется быстрее, однако я рекомендую вам подтвердить это с помощью сравнительного анализа. - person mpyw; 14.01.2019
comment
Это лучше, чем localeCompare по той причине, что localeCompare вернет 0 для строк, которые не равны. Конкретный пример: есть (по крайней мере) два разных символа Ö, которые выглядят одинаково, и localeCompare говорит, что они одинаковы, но они не работают === (даже в верхнем регистре). Итак, ваш симпатичный код пользовательского интерфейса может делать что-то, включая сортировку и группировку, и то, что… такое же, как…, но ваша внутренняя логика, использующая карту, решит, что эти два… разные, так что все обернется плохо. - person Kevin Frei; 17.01.2021

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

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;
});
person airportyh    schedule 09.09.2008
comment
Кстати, это не будет обрабатывать сравнения строк и чисел. Например: 'Z' ‹9 (ложь), 'Z'› 9 (тоже ложь ??), 'Z' == 9 (тоже ложь !!). Глупый NaN в JavaScript ... - person Kato; 18.07.2014

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

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

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

person Alejadro Xalabarder    schedule 22.09.2019

Вложенная тернарная стрелочная функция

(a,b) => (a < b ? -1 : a > b ? 1 : 0)
person geckos    schedule 14.03.2019

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

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

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. 

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

tl; dr;

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

person Manav    schedule 07.02.2013

Объяснение того, почему подход в вопросе не работает:

let products = [
    { name: "laptop", price: 800 },
    { name: "phone", price:200},
    { name: "tv", price: 1200}
];
products.sort( (a, b) => {
    {let value= a.name - b.name; console.log(value); return value}
});

> 2 NaN

Вычитание между строками возвращает NaN.

Повторяя ответ @Alejadro, правильный подход -

products.sort( (a,b) => a.name > b.name ? 1 : -1 )

person tash    schedule 30.10.2020

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
person Petr Varyagin    schedule 01.09.2016
comment
Ответы только на код можно сделать более полезными, объяснив, как они работают. - person Dan Dascalescu; 05.01.2018

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

item1.attr - item2.attr

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

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

Если они alphaNumeric, тогда используйте localCompare ().

person eggmatters    schedule 24.10.2013

Используйте sort () прямо без каких-либо - или <

const areas = ['hill', 'beach', 'desert', 'mountain']
console.log(areas.sort())

// To print in descending way
console.log(areas.sort().reverse())

person TrickOrTreat    schedule 31.01.2021

Должны быть функции по возрастанию и по убыванию

            if (order === 'asc') {
                return a.localeCompare(b);
            }
            return b.localeCompare(a);
person deathfry    schedule 24.06.2021

person    schedule
comment
Пожалуйста, добавьте к вашему ответу некоторую информацию о том, как это решит вопрос. Ответы только на код не приветствуются. Спасибо. - person wayneOS; 23.04.2018
comment
здесь вы хотите упорядочить символы в строке, чего не просят. Вы можете добиться такой сортировки, просто используя Array.sort, например. str.split (). sort () .join () - person Alejadro Xalabarder; 23.09.2019

person    schedule
comment
Хотя этот код может решить вопрос, включая объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, приведет к увеличению числа голосов "за". Помните, что вы отвечаете на вопрос для будущих читателей, а не только для человека, который задает его сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить пояснения и указать, какие ограничения и предположения применяются. - person Dave; 09.04.2019