У меня есть список объектов, которые я хочу отсортировать на основе поля attr типа string. Я пробовал использовать -
list.sort(function (a, b) {
return a.attr - b.attr
})
но обнаружил, что - не работает со строками в JavaScript. Как отсортировать список объектов по атрибуту со строкой типа?
Для быстрого «интернационализированного» решения (я думаю, только частично, поскольку это может не охватывать все акценты в мире) вы можете просто игнорировать акценты, то есть удалить их. Затем выполняйте только сравнение строк, см. Javascript : remove accents/diacritics in strings на stackoverflow.com/questions/990904/…
Достаточно забавно, что сам Джефф Этвуд написал в блоге сообщение об этой распространенной проблеме еще в 2007 году, см. blog.codinghorror.com/sorting-for-humans-natural-sort-order



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Используйте 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.
Первое решение будет считать, что «A» идет после «z», но перед «Z», поскольку оно выполняет сравнение значения ASCII символа. localeCompare() не сталкивается с этой проблемой, но не понимает числовые значения, поэтому вы получите ["1", "10", "2"], как и при сортировке сравнений на большинстве языков. если вы хотите сортировку для своего пользовательского интерфейса, изучите алгоритм буквенной / естественной сортировки stackoverflow.com/questions/4340227/… или stackoverflow.com/questions/4321829/…
Обратите внимание, что localeCompare() поддерживается только в современных браузерах: IE11 + на момент написания, см. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Можно заметить, что это вопрос о Natural Sort Algorithm .... см. stackoverflow.com/questions/2802341/…
Нет, я имею в виду первую строку таблицы, @Adrien - IE поддерживает localeCompare() во многих версиях, но не поддерживает указание локали до версии 11. Обратите также внимание на вопросы, с которыми связан Dead.Rabit.
@ 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
@ Shog9 еще раз спасибо за вашу помощь, после некоторых эпических исследований я добавил свой ответ, так как думал, что это может понравиться сообществу, см. Ниже.
См. Мой соответствующий ответ о том, как localeCompare() ведет себя с разными языковыми настройками. stackoverflow.com/a/59443357/1148030
Вы должны использовать здесь> или <и ==. Итак, решение было бы таким:
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 ...
Меня это беспокоило давно, поэтому я, наконец, исследовал это и дал вам длинную причину того, почему все так, как есть.
Из спецификация:
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
Ответы только на код можно сделать более полезными, объяснив, как они работают.
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 -> 1false -> 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;
Как я прокомментировал предыдущий ответ, который использовал этот трюк, ответы, содержащие только код, можно сделать более полезными, объяснив, как они работают.
Добавлено описание
Можете ли вы прокомментировать, лучше это или хуже, чем localeCompare?
@RanLottem localeCompare и стандартное сравнение дают разные результаты. Чего вы ожидаете? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b)) сортирует в алфавитном порядке без учета регистра, в то время как ["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b)) выполняет сортировку по кодовым точкам.
Я понимаю, это, кажется, главный камень преткновения. Есть идеи о различиях в производительности?
Я предполагаю, что порядок кодовых точек выполняется быстрее, однако я рекомендую вам подтвердить это с помощью сравнительного анализа.
Это лучше, чем localeCompare по той причине, что localeCompare вернет 0 для строк, которые не равны. Конкретный пример: есть (по крайней мере) два разных символа «Ö», которые выглядят одинаково, и localeCompare говорит, что они одинаковы, но они не работают === (даже в верхнем регистре). Итак, ваш симпатичный код пользовательского интерфейса может делать что-то, включая сортировку и группировку, и то, что «Ö» совпадает с «Ö», но ваша внутренняя логика, использующая Map, решит, что эти два Ö различны, так что все обернется плохо.
<!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>
Пожалуйста, добавьте к вашему ответу некоторую информацию о том, как это решит вопрос. Ответы только на код не приветствуются. Спасибо.
здесь вы хотите упорядочить символы в строке, чего не просят. Вы можете добиться такой сортировки, просто используя "Array.sort", например. str.split (""). sort () .join ("")
Вложенная тернарная стрелочная функция
(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)
Хотя этот код может решить вопрос, включая объяснение о том, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, приведет к большему количеству голосов за. Помните, что вы отвечаете на вопрос для будущих читателей, а не только для человека, который задает его сейчас. Пожалуйста, редактировать свой ответ, чтобы добавить пояснение и указать, какие ограничения и допущения применяются.
поскольку строки можно сравнивать непосредственно в 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())
см.
JavaScript case insensitive string comparisonна stackoverflow.com/questions/2140627/…