Как выполнить сортировку без учета регистра в JavaScript?

220

У меня есть массив строк, которые мне нужно отсортировать в 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'});
Michael Dyck
2
+1 за то, что не звонит, toLowerCase()когда localeCompareуже делает это по умолчанию в некоторых случаях. Вы можете прочитать больше о параметрах для передачи здесь: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Milimetric
3
@Milimetric согласно ссылочной странице, эта функция не поддерживается некоторыми браузерами (например, IE <11 или Safari). решение, упомянутое здесь, очень хорошее, но для некоторых браузеров все равно требуется обратная портирование / полифиллинг.
3k-
2
Если у вас большой массив, имеет смысл использовать его items.sort(new Intl.Collator('en').compare)для повышения производительности. (См. MDN .)
Вальтлай
60
myArray.sort(
  function(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;
  }
);

РЕДАКТИРОВАТЬ: Обратите внимание, что я изначально написал это, чтобы проиллюстрировать технику, а не иметь в виду производительность. Пожалуйста, также обратитесь к ответу @Ivan Krechetov для более компактного решения.

Рон Торнамбе
источник
3
Это может вызвать toLowerCaseдважды в каждой строке; было бы более эффективно хранить пониженные версии строки в переменных.
Джейкоб
Правда и спасибо. Я написал это с ясностью, а не производительность. Думаю, я должен это отметить.
Рон Торнамбе
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опции.

ZunTzu
источник
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"]
mateuscb
источник
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, вероятно, хорошо, хотя ...

Примечание. Оператор Элвиса - это краткая форма «троичный оператор», если не всегда, обычно с присваиванием.
Если вы посмотрите на?: Sideway, это будет похоже на Элвиса ...
т.е. вместо:

if (y) {
  x = 1;
} else {
  x = 2;
}

ты можешь использовать:

x = y?1:2;

т.е. когда y истинно, тогда возвращают 1 (для присваивания x), иначе возвращают 2 (для присваивания x).

AndyS
источник
5
Чтобы быть педантичным, это не оператор Элвиса. Это просто основной троичный оператор. Истинный оператор Элвиса является объединяющим нулем, например, вместо x = y ? y : z, вы можете сделать x = y ?: z. Javascript не имеет фактического оператора Элвиса, но вы можете использовать x = y || zаналогичным образом.
Чарльз Вуд
3

Другие ответы предполагают, что массив содержит строки. Мой метод лучше, потому что он будет работать, даже если массив содержит 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"
MattW
Думаю, мне следовало найти определение Array.prototype.sort: | потому что часть about (''+notdefined) === "undefined" действительно true ... что означает, что если вы перевернете -1 и 1 в функции сортировки, чтобы изменить порядок, неопределенное все равно будет сортироваться до конца. Это также необходимо учитывать при использовании функции сравнения вне контекста сортировки по массиву (как я это делал, когда натолкнулся на этот вопрос).
MattW
А теперь обдумав это Array.prototype.sortопределение - еще пара комментариев. Во-первых, нет необходимости (''+a)- ECMAScript требует вызова toString()элементов перед передачей их в compareFn. Во-вторых, тот факт, что ignoreCaseвозвращается 1при сравнении одинаковых (в том числе равных, но для случая) строк, означает, что спецификация не определяет результат, если есть повторяющиеся значения (вероятно, будет хорошо только при некоторых ненужных перестановках, я думаю).
MattW
@ MattW, мне кажется, что undefinedэто особый случай, который для любого x x <undefined и x> undefined имеет значение false . Это undefinedвсегда последнее, является побочным продуктом реализации sort. Я пытался изменить ('' + a) на просто a, но это не удалось. я получаю TypeError: a.toUpperCase is not a function. По-видимому toString, не вызывается до вызова CompareFn.
Джон Хенкель
1
Ах, хорошо, это имеет смысл. Для undefinedсравнения Fn никогда не называется
Джон Хенкель
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());
        });
    };
}
Джейсон
источник
Пожалуйста, никогда не делай этого. Изменяйте только прототип вещей, которыми вы владеете. Это также не polyfill, так как этот метод Array нигде в спецификации ECMAScript.
Джо Маффей
-2

Оберните свои строки в / /i. Это простой способ использовать регулярные выражения, чтобы игнорировать обсадную колонну

user3225968
источник
Вопрос в сортировке, а не в совпадении.
user4642212