У меня есть массив строк, которые мне нужно отсортировать в JavaScript, но без учета регистра. Как это сделать?
У меня есть массив строк, которые мне нужно отсортировать в JavaScript, но без учета регистра. Как это сделать?
Ответы:
В (почти :) один вкладыш
["Foo", "bar"].sort(function (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
Что приводит к
[ 'bar', 'Foo' ]
Пока
["Foo", "bar"].sort();
результаты в
[ 'Foo', 'bar' ]
return a.localeCompare(b, 'en', {'sensitivity': 'base'});
toLowerCase()
когда localeCompare
уже делает это по умолчанию в некоторых случаях. Вы можете прочитать больше о параметрах для передачи здесь: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
myArray.sort(
function(a, b) {
if (a.toLowerCase() < b.toLowerCase()) return -1;
if (a.toLowerCase() > b.toLowerCase()) return 1;
return 0;
}
);
РЕДАКТИРОВАТЬ: Обратите внимание, что я изначально написал это, чтобы проиллюстрировать технику, а не иметь в виду производительность. Пожалуйста, также обратитесь к ответу @Ivan Krechetov для более компактного решения.
toLowerCase
дважды в каждой строке; было бы более эффективно хранить пониженные версии строки в переменных.
.toLowerCase()
несколько раз для каждого элемента в массиве. Например, 45 вызовов функции сравнения при сортировке 10 элементов в обратном порядке. var i = 0; ["z","y","x","w","v","u","t","s","r","q"].sort(function (a, b) {++i; return a.toLowerCase().localeCompare(b.toLowerCase());}); console.log("Calls to Compare: " + i); // i === 45
Настало время вернуться к этому старому вопросу.
Вы не должны использовать решения, основанные на toLowerCase
. Они неэффективны и просто не работают на некоторых языках (например, на турецком). Предпочитаю это:
['Foo', 'bar'].sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}))
Проверьте документацию на совместимость браузера и все, что нужно знать об этой sensitivity
опции.
arr.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a == b) return 0;
if (a > b) return 1;
return -1;
});
return a === b ? 0 : a > b ? 1 : -1;
["111", "33"]
, мы могли бы хотеть, чтобы это возвратилось, ["111", "33"]
потому что 1 предшествует 3 в порядке кодировки символов. Тем не менее, функция в этом ответе вернется, ["33", "111"]
потому что число 33
меньше, чем число 111
.
"33" > "111" === true
и 33 > 111 === false
. Работает как задумано.
Вы также можете использовать новый Intl.Collator().compare
, согласно MDN, он более эффективен при сортировке массивов. Недостатком является то, что он не поддерживается старыми браузерами. MDN заявляет, что он вообще не поддерживается в Safari. Нужно проверить это, так как в нем говорится, что Intl.Collator
поддерживается.
При сравнении большого количества строк, например при сортировке больших массивов, лучше создать объект Intl.Collator и использовать функцию, предоставляемую его свойством сравнения.
["Foo", "bar"].sort(Intl.Collator().compare); //["bar", "Foo"]
Если вы хотите гарантировать один и тот же порядок независимо от порядка элементов во входном массиве, вот стабильная сортировка:
myArray.sort(function(a, b) {
/* Storing case insensitive comparison */
var comparison = a.toLowerCase().localeCompare(b.toLowerCase());
/* If strings are equal in case insensitive comparison */
if (comparison === 0) {
/* Return case sensitive comparison instead */
return a.localeCompare(b);
}
/* Otherwise return result */
return comparison;
});
Нормализовать регистр в .sort()
с .toLowerCase()
.
Вы также можете использовать оператор Elvis:
arr = ['Bob', 'charley', 'fudge', 'Fudge', 'biscuit'];
arr.sort(function(s1, s2){
var l=s1.toLowerCase(), m=s2.toLowerCase();
return l===m?0:l>m?1:-1;
});
console.log(arr);
дает:
biscuit,Bob,charley,fudge,Fudge
Метод localeCompare, вероятно, хорошо, хотя ...
Примечание. Оператор Элвиса - это краткая форма «троичный оператор», если не всегда, обычно с присваиванием.
Если вы посмотрите на?: Sideway, это будет похоже на Элвиса ...
т.е. вместо:
if (y) {
x = 1;
} else {
x = 2;
}
ты можешь использовать:
x = y?1:2;
т.е. когда y истинно, тогда возвращают 1 (для присваивания x), иначе возвращают 2 (для присваивания x).
x = y ? y : z
, вы можете сделать x = y ?: z
. Javascript не имеет фактического оператора Элвиса, но вы можете использовать x = y || z
аналогичным образом.
Другие ответы предполагают, что массив содержит строки. Мой метод лучше, потому что он будет работать, даже если массив содержит null, undefined или другие нестандартные значения.
var notdefined;
var myarray = ['a', 'c', null, notdefined, 'nulk', 'BYE', 'nulm'];
myarray.sort(ignoreCase);
alert(JSON.stringify(myarray)); // show the result
function ignoreCase(a,b) {
return (''+a).toUpperCase() < (''+b).toUpperCase() ? -1 : 1;
}
null
Будет отсортирован между «Нульк» и «nulm». Но последний всегдаundefined
будет отсортирован.
(''+notdefined) === "undefined"
так что это будет сортировать до "Z"
Array.prototype.sort
: | потому что часть about (''+notdefined) === "undefined"
действительно true ... что означает, что если вы перевернете -1 и 1 в функции сортировки, чтобы изменить порядок, неопределенное все равно будет сортироваться до конца. Это также необходимо учитывать при использовании функции сравнения вне контекста сортировки по массиву (как я это делал, когда натолкнулся на этот вопрос).
Array.prototype.sort
определение - еще пара комментариев. Во-первых, нет необходимости (''+a)
- ECMAScript требует вызова toString()
элементов перед передачей их в compareFn. Во-вторых, тот факт, что ignoreCase
возвращается 1
при сравнении одинаковых (в том числе равных, но для случая) строк, означает, что спецификация не определяет результат, если есть повторяющиеся значения (вероятно, будет хорошо только при некоторых ненужных перестановках, я думаю).
undefined
это особый случай, который для любого x x <undefined и x> undefined имеет значение false . Это undefined
всегда последнее, является побочным продуктом реализации sort. Я пытался изменить ('' + a) на просто a, но это не удалось. я получаю TypeError: a.toUpperCase is not a function
. По-видимому toString
, не вызывается до вызова CompareFn.
undefined
сравнения Fn никогда не называется
Версия ES6:
["Foo", "bar"].sort((a, b) => a.localeCompare(b, 'en', { sensitivity: 'base' }))
В поддержку принятого ответа я хотел бы добавить, что приведенная ниже функция, по-видимому, изменяет значения в исходном массиве для сортировки, так что она не только сортирует нижний регистр, но и значения верхнего регистра также изменяются на нижний регистр. Это проблема для меня, потому что хотя я хочу видеть Мэри рядом с Мэри, я не хочу, чтобы регистр первого значения Мэри был изменен на нижний регистр.
myArray.sort(
function(a, b) {
if (a.toLowerCase() < b.toLowerCase()) return -1;
if (a.toLowerCase() > b.toLowerCase()) return 1;
return 0;
}
);
В моих экспериментах следующая функция из принятого ответа сортирует правильно, но не меняет значения.
["Foo", "bar"].sort(function (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
Это может помочь, если вы пытались понять:
var array = ["sort", "Me", "alphabetically", "But", "Ignore", "case"];
console.log('Unordered array ---', array, '------------');
array.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
console.log("Compare '" + a + "' and '" + b + "'");
if( a == b) {
console.log('Comparison result, 0 --- leave as is ');
return 0;
}
if( a > b) {
console.log('Comparison result, 1 --- move '+b+' to before '+a+' ');
return 1;
}
console.log('Comparison result, -1 --- move '+a+' to before '+b+' ');
return -1;
});
console.log('Ordered array ---', array, '------------');
// return logic
/***
If compareFunction(a, b) is less than 0, sort a to a lower index than b, i.e. a comes first.
If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
If compareFunction(a, b) is greater than 0, sort b to a lower index than a.
***/
arr.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
if( a == b) return 0;
if( a > b) return 1;
return -1;
});
В приведенной выше функции, если мы просто сравниваем в нижнем регистре два значения a и b, мы не получим симпатичного результата.
Например, если массив [A, a, B, b, c, C, D, d, e, E] и мы используем вышеупомянутую функцию, у нас есть именно этот массив. Это ничего не изменило.
Чтобы получить результат [A, a, B, b, C, c, D, d, E, e], мы должны снова сравнить, когда два строчных значения равны:
function caseInsensitiveComparator(valueA, valueB) {
var valueALowerCase = valueA.toLowerCase();
var valueBLowerCase = valueB.toLowerCase();
if (valueALowerCase < valueBLowerCase) {
return -1;
} else if (valueALowerCase > valueBLowerCase) {
return 1;
} else { //valueALowerCase === valueBLowerCase
if (valueA < valueB) {
return -1;
} else if (valueA > valueB) {
return 1;
} else {
return 0;
}
}
}
Я завернул верхний ответ в полизаполнение, чтобы я мог вызвать .sortIgnoreCase () для строковых массивов
// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
Array.prototype.sortIgnoreCase = function () {
return this.sort(function (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
};
}
Оберните свои строки в / /i
. Это простой способ использовать регулярные выражения, чтобы игнорировать обсадную колонну