Как сортировать строки в JavaScript - PullRequest
275 голосов
/ 09 сентября 2008

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

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

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

Ответы [ 10 ]

485 голосов
/ 09 сентября 2008

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

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;
137 голосов
/ 10 октября 2014

Обновленный ответ (октябрь 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, который, как мне кажется, поставил меня в «правильное» направление

22 голосов
/ 01 ноября 2016

Ответ (в современном 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: (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

(Alternative)

  • х больше чем у: +(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;
9 голосов
/ 09 сентября 2008

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

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;
});
5 голосов
/ 07 февраля 2013

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

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

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, поскольку они, например, являются внутренними программными константами, то === также работает.

1 голос
/ 14 марта 2019

Функция вложенной троичной стрелки

(a,b) => (a < b ? -1 : a > b ? 1 : 0)
0 голосов
/ 09 апреля 2019
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)
0 голосов
/ 23 апреля 2018
<!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>
0 голосов
/ 02 сентября 2016
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
0 голосов
/ 24 октября 2013

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

item1.attr - item2.attr

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...