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

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

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

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

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

Adrien Be 26.09.2014 14:37

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

Adrien Be 26.09.2014 15:00

Достаточно забавно, что сам Джефф Этвуд написал в блоге сообщение об этой распространенной проблеме еще в 2007 году, см. blog.codinghorror.com/sorting-for-humans-natural-sort-order

Adrien Be 26.09.2014 16:02
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
398
3
411 210
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

Ответ принят как подходящий

Используйте 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;

Прежде чем кто-либо совершит такую ​​же поспешную ошибку, как я, это localеCompare, а не localCompare.

ento 09.09.2012 13:38

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

Dead.Rabit 20.06.2013 17:22

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

Adrien Be 26.09.2014 14:39

Можно заметить, что это вопрос о Natural Sort Algorithm .... см. stackoverflow.com/questions/2802341/…

Adrien Be 26.09.2014 19:33

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

Shog9 26.09.2014 21:54

@ 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

Adrien Be 29.09.2014 11:56

@ Shog9 еще раз спасибо за вашу помощь, после некоторых эпических исследований я добавил свой ответ, так как думал, что это может понравиться сообществу, см. Ниже.

Adrien Be 10.10.2014 12:44

См. Мой соответствующий ответ о том, как localeCompare() ведет себя с разными языковыми настройками. stackoverflow.com/a/59443357/1148030

Peter Lamberg 22.12.2019 16:40

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

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

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

Kato 18.07.2014 21:20

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

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

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.

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

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

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

item1.attr - item2.attr

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

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

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

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

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

Dan Dascalescu 05.01.2018 13:10

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

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

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

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

Dan Dascalescu 30.10.2018 20:31

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

mpyw 04.11.2018 08:33

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

Ran Lottem 13.01.2019 14:35

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

mpyw 13.01.2019 15:11

Я понимаю, это, кажется, главный камень преткновения. Есть идеи о различиях в производительности?

Ran Lottem 13.01.2019 16:16

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

mpyw 14.01.2019 15:32

Это лучше, чем localeCompare по той причине, что localeCompare вернет 0 для строк, которые не равны. Конкретный пример: есть (по крайней мере) два разных символа «Ö», которые выглядят одинаково, и localeCompare говорит, что они одинаковы, но они не работают === (даже в верхнем регистре). Итак, ваш симпатичный код пользовательского интерфейса может делать что-то, включая сортировку и группировку, и то, что «Ö» совпадает с «Ö», но ваша внутренняя логика, использующая Map, решит, что эти два Ö различны, так что все обернется плохо.

Kevin Frei 17.01.2021 02:35

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

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

wayneOS 23.04.2018 23:55

здесь вы хотите упорядочить символы в строке, чего не просят. Вы можете добиться такой сортировки, просто используя "Array.sort", например. str.split (""). sort () .join ("")

Alejadro Xalabarder 23.09.2019 02:17

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

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

var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.info(b)

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

Dave 09.04.2019 16:18

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

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

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

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

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.info(value); return value}
});

> 2 NaN

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

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

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

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

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

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

Другие вопросы по теме