У мене є масив рядків, які мені потрібно сортувати в 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;
}
);
EDIT: Зауважте, що я спочатку писав це, щоб проілюструвати техніку, а не мати на увазі продуктивність. Також зверніться до відповіді @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, ймовірно, добре, хоча ...
Примітка: Оператор Елвіса є короткою формою «потрійний оператор», якщо тоді, як правило, із завданням.
Якщо ви дивитесь на:: збоку, це виглядає як Елвіс ...
тобто замість:
if (y) {
x = 1;
} else {
x = 2;
}
Ви можете використовувати:
x = y?1:2;
тобто, коли y відповідає дійсності, тоді поверніться 1 (для призначення x), інакше поверніть 2 (для призначення x).
x = y ? y : z
можна зробити x = y ?: z
. У Javascript немає власного оператора Elvis, але його можна використовувати x = y || z
аналогічно.
Інші відповіді передбачають, що масив містить рядки. Мій метод кращий, тому що він буде працювати, навіть якщо масив містить нульові, невизначені чи інші не рядки.
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
: | тому що частина (''+notdefined) === "undefined"
справді справжня ... це означає, що якщо перегорнути -1 і 1 у функції сортування, щоб змінити порядок, невизначений все одно сортує до кінця. Це також слід враховувати при використанні функції порівняння поза контекстом сортування масиву (як я був, коли я стикався з цим питанням).
Array.prototype.sort
визначенням - ще пару коментарів. По-перше, немає необхідності в тому, що (''+a)
ECMAScript вимагає toString()
викликати елементи перед тим, як передати їх у CompareFn. По-друге, той факт, що ignoreCase
повертається 1
при порівнянні рівних (у тому числі рівних, але для випадку) рядків, означає, що специфікація не визначає результат, якщо є дублікати значень (напевно, я думаю, що з непотрібними замінами, я думаю, що).
undefined
це особливий випадок, який для будь-яких x x <undefined та x> undefined є хибними . Це undefined
завжди останнє, є побічним продуктом здійснення сортування. Я намагався змінити ('' + 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
. Це простий спосіб використання регулярного вираження для ігнорування корпусу