Як здійснити сортування, що не враховує регістр у JavaScript?


Відповіді:


404

В (майже :) однолінійний

["Foo", "bar"].sort(function (a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
});

Які результати в

[ 'bar', 'Foo' ]

Поки

["Foo", "bar"].sort();

призводить до

[ 'Foo', 'bar' ]

9
Майте на увазі, що розширені параметри localeCompare ще не підтримуються на всіх платформах / браузерах. Я знаю, що вони не використовуються в цьому прикладі, а просто хотіли додати для ясності. Дивіться MDN для отримання додаткової інформації
Ayame__

97
Якщо ви збираєтесь залучати localeCompare (), ви можете просто використовувати його здатність бути нечутливими до регістру, наприклад:return a.localeCompare(b, 'en', {'sensitivity': 'base'});
Майкл Дік

2
+1 для того, щоб не дзвонити, toLowerCase()коли це localeCompareвже робиться за замовчуванням у деяких випадках. Детальніше про параметри для переходу до нього можна прочитати тут: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Milimetric

3
@ Міліметрична згода на вказану сторінку, ця функція не підтримується деякими браузерами (наприклад, IE <11 або Safari). рішення, згадане тут, дуже добре, але все-таки вимагатиме резервного копіювання / полізаповнення для деяких браузерів.
3к.-

2
Якщо у вас є великий масив, це має сенс використовувати items.sort(new Intl.Collator('en').compare)для кращої продуктивності. (Див. MDN )
valtlai

60
myArray.sort(
  function(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;
  }
);

EDIT: Зауважте, що я спочатку писав це, щоб проілюструвати техніку, а не мати на увазі продуктивність. Також зверніться до відповіді @Ivan Krechetov для більш компактного рішення.


3
Це може зателефонувати в toLowerCaseдва рази по кожному рядку; було б більш ефективно зберігати знижені версії рядка в змінних.
Яків

Правда і спасибі Я написав це з ясністю на увазі, а не виконанням. Я думаю, що це слід зазначити.
ron tornambe

1
@Jacob Для справедливості прийнята відповідь має одну і ту ж основну проблему: вона, можливо, може зателефонувати .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
нічогонеобхідного

47

Настав час переглянути це старе питання.

Не слід використовувати рішення, на які покладаються toLowerCase. Вони неефективні і просто не працюють на деяких мовах (наприклад, турецька). Віддайте перевагу цьому:

['Foo', 'bar'].sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}))

Перевірте документацію для сумісності браузера і все, що потрібно знати про sensitivityопції.


1
Будьте уважні, це підтримується не в усіх системах javascript.
Любош Турек

26
arr.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if (a == b) return 0;
    if (a > b) return 1;
    return -1;
});

1
абоreturn a === b ? 0 : a > b ? 1 : -1;
Девін Г Род

Це, ймовірно, не буде працювати як призначено для рядків, що представляють числа. Арифметичні оператори використовуватимуть семантику чисел замість рядків. Наприклад, якщо у нас є ["111", "33"], ми можемо захотіти, щоб він повернувся, ["111", "33"]тому що 1 входить до 3 в порядку впорядкування коду символів. Однак функція у цій відповіді повернеться, ["33", "111"]оскільки число 33менше числа 111.
Остін Девіс

@AustinDavis "33" > "111" === trueі 33 > 111 === false. Це працює за призначенням.
Niet the Dark Absol

12

Ви також можете використовувати новий Intl.Collator().compare, на MDN, він більш ефективний при сортуванні масивів. Мінусом є те, що він не підтримується старими браузерами. MDN заявляє, що в Safari його взагалі не підтримують. Потрібно перевірити це, оскільки він стверджує, що Intl.Collatorпідтримується.

Порівнюючи велику кількість рядків, наприклад, при сортуванні великих масивів, краще створити об’єкт Intl.Collator і використовувати функцію, надану його властивістю порівняння

["Foo", "bar"].sort(Intl.Collator().compare); //["bar", "Foo"]

11

Якщо ви хочете гарантувати той самий порядок незалежно від порядку елементів вхідного масиву, ось стабільне сортування:

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

5

Нормалізуйте випадок у .sort()с .toLowerCase().


4

Ви також можете скористатися оператором 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).


5
Щоб бути педантичним, це не оператор Елвіса. Це лише базовий потрійний оператор. Справжній оператор Елвіса з’єднує нуль, наприклад, замість цього x = y ? y : zможна зробити x = y ?: z. У Javascript немає власного оператора Elvis, але його можна використовувати x = y || zаналогічно.
Чарльз Вуд

3

Інші відповіді передбачають, що масив містить рядки. Мій метод кращий, тому що він буде працювати, навіть якщо масив містить нульові, невизначені чи інші не рядки.

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"
MattW

Здогадуюсь, я повинен був би знайти визначення Array.prototype.sort: | тому що частина (''+notdefined) === "undefined" справді справжня ... це означає, що якщо перегорнути -1 і 1 у функції сортування, щоб змінити порядок, невизначений все одно сортує до кінця. Це також слід враховувати при використанні функції порівняння поза контекстом сортування масиву (як я був, коли я стикався з цим питанням).
MattW

А тепер задумавшись над цим Array.prototype.sortвизначенням - ще пару коментарів. По-перше, немає необхідності в тому, що (''+a)ECMAScript вимагає toString()викликати елементи перед тим, як передати їх у CompareFn. По-друге, той факт, що ignoreCaseповертається 1при порівнянні рівних (у тому числі рівних, але для випадку) рядків, означає, що специфікація не визначає результат, якщо є дублікати значень (напевно, я думаю, що з непотрібними замінами, я думаю, що).
MattW

@MattW, мені здається, undefinedце особливий випадок, який для будь-яких x x <undefined та x> undefined є хибними . Це undefinedзавжди останнє, є побічним продуктом здійснення сортування. Я намагався змінити ('' + a) на просто a, але це не вдається. я отримую TypeError: a.toUpperCase is not a function. Мабуть toString, не викликається до виклику CompareFn.
Джон Генкель

1
Ах, добре, це має ідеальний сенс. Для undefinedпорівнянняFn ніколи не називається
John Henckel


1

На підтримку прийнятої відповіді хотілося б додати, що функція нижче, здається, змінює значення в початковому масиві для сортування, щоб не тільки сортувати нижній регістр, але і великі великі регістри також буде змінено на нижній регістр. Це проблема для мене, оскільки, хоч я хочу бачити Марію поруч із Мері, я не хочу, щоб справа першої цінності Марії була змінена на нижню.

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

0

Це може допомогти, якщо ви намагаєтесь зрозуміти:

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.
***/

http://jsfiddle.net/ianjamieson/wmxn2ram/1/


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

-1

Я загорнув верхню відповідь у поліфайл, щоб я міг викликати .sortIgnoreCase () на рядкових масивах

// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
    Array.prototype.sortIgnoreCase = function () {
        return this.sort(function (a, b) {
            return a.toLowerCase().localeCompare(b.toLowerCase());
        });
    };
}

Будь ласка, ніколи цього не роби. Змінюйте лише прототип речей, якими ви володієте. Це також не є заповненням, оскільки цього методу масиву немає в специфікаціях ECMAScript.
Джо Маффей

-2

Загорніть свої пасма / /i. Це простий спосіб використання регулярного вираження для ігнорування корпусу


Питання полягає в сортуванні, а не в збігу.
user4642212
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.