Como executar a classificação que não diferencia maiúsculas de minúsculas no JavaScript?

220

Eu tenho uma variedade de strings que preciso classificar em JavaScript, mas de uma maneira que não diferencia maiúsculas de minúsculas. Como fazer isso?

Jérôme Verstrynge
fonte

Respostas:

404

Em (quase :) um one-liner

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

O que resulta em

[ 'bar', 'Foo' ]

Enquanto

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

resulta em

[ 'Foo', 'bar' ]
Ivan Krechetov
fonte
9
Lembre-se de que as opções avançadas do localeCompare ainda não são suportadas em todas as plataformas / navegadores. Eu sei que eles não são usados ​​neste exemplo, mas só quero acrescentar para maior clareza. Veja MDN para obter mais informações
Ayame__
97
Se você envolver o localeCompare (), poderá usar sua capacidade de fazer distinção entre maiúsculas e minúsculas, por exemplo:return a.localeCompare(b, 'en', {'sensitivity': 'base'});
Michael Dyck
2
+1 por não ligar toLowerCase()quando localeComparejá o faz por padrão em alguns casos. Você pode ler mais sobre os parâmetros a serem transmitidos aqui: developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
Milimetric
3
@ De acordo com a página referenciada, esse recurso não é suportado por alguns navegadores (por exemplo, IE <11 ou Safari). a solução mencionada aqui é muito boa, mas ainda exigiria backporting / polyfill para alguns navegadores.
3k-
2
Se você tem uma grande variedade, faz sentido usar items.sort(new Intl.Collator('en').compare)para obter melhor desempenho. (Veja MDN .)
valtlai
60
myArray.sort(
  function(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;
  }
);

Edição: Por favor, note que eu escrevi isso originalmente para ilustrar a técnica, em vez de ter o desempenho em mente. Consulte também a resposta a @Ivan Krechetov para obter uma solução mais compacta.

Ron Tornambe
fonte
3
Isso pode chamar toLowerCaseduas vezes em cada string; seria mais eficiente armazenar versões reduzidas da string em variáveis.
Jacob
Verdadeiro e obrigado. Eu escrevi isso com clareza em mente, não desempenho. Acho que devo notar isso.
Ron tornambe
1
@ Jacob Para ser justo, a resposta aceita tem o mesmo problema básico: pode ligar .toLowerCase()várias vezes para cada item da matriz. Por exemplo, 45 chama a função comparar ao classificar 10 itens na ordem inversa. 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
nothingisnecessary
47

É hora de revisitar essa velha questão.

Você não deve usar soluções confiáveis toLowerCase. Eles são ineficientes e simplesmente não funcionam em alguns idiomas (turco, por exemplo). Prefira isto:

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

Verifique a documentação quanto à compatibilidade do navegador e tudo o que há para saber sobre a sensitivityopção.

ZunTzu
fonte
1
Cuidado, isso não é suportado em todos os mecanismos javascript.
Luboš Turek
26
arr.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if (a == b) return 0;
    if (a > b) return 1;
    return -1;
});
Niet, o Escuro Absol
fonte
1
oureturn a === b ? 0 : a > b ? 1 : -1;
Devin G Rhode
Provavelmente, isso não funcionará como planejado para cadeias que representam números. Os operadores aritméticos usarão a semântica dos números em vez de cadeias. Por exemplo, se tivermos ["111", "33"], podemos querer que retorne ["111", "33"]porque 1 vem antes de 3 na ordenação do código de caracteres. No entanto, a função nesta resposta retornará ["33", "111"]porque o número 33é menor que o número 111.
Austin Davis
@AustinDavis "33" > "111" === truee 33 > 111 === false. Funciona como pretendido.
Niet the Dark Absol
12

Você também pode usar o novo Intl.Collator().compare, por MDN, é mais eficiente na classificação de matrizes. A desvantagem é que não é suportado por navegadores mais antigos. O MDN afirma que não é suportado no Safari. Precisa verificar, pois afirma que Intl.Collatoré suportado.

Ao comparar um grande número de strings, como na classificação de matrizes grandes, é melhor criar um objeto Intl.Collator e usar a função fornecida por sua propriedade compare

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

Se você deseja garantir a mesma ordem, independentemente da ordem dos elementos na matriz de entrada, aqui está uma classificação estável :

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;
});
Aalex Gabi
fonte
5

Normalize o caso .sort()com .toLowerCase().


fonte
4

Você também pode usar o operador 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);

Dá:

biscuit,Bob,charley,fudge,Fudge

O método localeCompare provavelmente é bom ...

Nota: O operador Elvis é uma forma abreviada de 'operador ternário', caso contrário, geralmente com atribuição.
Se você olhar para?: De lado, parece Elvis ...
ou seja, em vez de:

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

você pode usar:

x = y?1:2;

ou seja, quando y for verdadeiro, retorne 1 (para a atribuição a x), caso contrário, retorne 2 (para a atribuição a x).

AndyS
fonte
5
Para ser pedante, este não é o operador Elvis. Este é apenas um operador ternário básico. Um verdadeiro operador de Elvis é coalescente nulo, por exemplo, em vez de x = y ? y : z, você pode fazer x = y ?: z. O Javascript não possui um operador Elvis real, mas você pode usá-lo de x = y || zmaneira semelhante.
Charles Wood
3

As outras respostas assumem que a matriz contém seqüências de caracteres. Meu método é melhor, porque funcionará mesmo que a matriz contenha nulas, indefinidas ou outras não-seqüências de caracteres.

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

O nullserá classificado entre 'nulk' e 'nulm'. Mas o undefinedserá sempre classificado por último.

John Henckel
fonte
(''+notdefined) === "undefined"por isso gostaria classificar antes de "z"
MattW
Acho que eu deveria ter procurado a definição de Array.prototype.sort: | porque a parte sobre (''+notdefined) === "undefined" realmente é verdadeira ... o que significa que se você girar o -1 e 1 na função de classificação para reverter a ordem, indefinido ainda será classificado até o fim. Também precisa ser considerado ao usar a função de comparação fora do contexto de uma classificação de matriz (como eu era quando me deparei com essa pergunta).
MattW
E, agora, ponderando essa Array.prototype.sortdefinição - faça mais alguns comentários. Primeiro, não há necessidade de o (''+a)- ECMAScript precisar toString()ser chamado nos elementos antes de passá-los para compareFn. Segundo, o fato de ignoreCaseretornar 1ao comparar seqüências de caracteres iguais (incluindo iguais mas para o caso) significa que a especificação não define o resultado se houver valores duplicados (provavelmente será bom apenas com algumas trocas desnecessárias, eu acho).
MattW
@ MattW, parece-me que undefinedé um caso especial, que para qualquer x x <indefinido ex > indefinido são ambos falsos . Isso undefinedé sempre o último, é um subproduto da implementação de classificação de classificação. Tentei mudar o ('' + a) para simplesmente a, mas ele falhou. eu entendo TypeError: a.toUpperCase is not a function. Aparentemente, nãotoString é chamado antes de chamar compareFn.
John Henckel
1
Ah, ok, isso faz todo o sentido. Para undefinedo compareFn nunca
John Henckel
1

Em apoio à resposta aceita, gostaria de acrescentar que a função abaixo parece alterar os valores na matriz original a serem classificados, para que não apenas classifique em minúsculas, mas também em maiúsculas. Isso é um problema para mim, porque, embora eu deseje ver Mary próxima a Mary, não desejo que o caso do primeiro valor Mary seja alterado para minúsculo.

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

Nas minhas experiências, a seguinte função da resposta aceita é classificada corretamente, mas não altera os valores.

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

Isso pode ajudar se você tiver se esforçado para entender:

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/

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

Na função acima, se apenas compararmos quando as minúsculas dois valor a e b, não teremos o resultado bonito.

Exemplo, se a matriz é [A, a, B, b, c, C, D, d, e, E] e usamos a função acima, temos exatamente essa matriz. Não mudou nada.

Para que o resultado seja [A, a, B, b, C, c, D, d, E, e], devemos comparar novamente quando dois valores em minúsculas forem iguais:

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;
        }
    }
}
Inveja
fonte
-1

Embrulhei a resposta principal em um polyfill para poder chamar .sortIgnoreCase () em matrizes de string

// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
    Array.prototype.sortIgnoreCase = function () {
        return this.sort(function (a, b) {
            return a.toLowerCase().localeCompare(b.toLowerCase());
        });
    };
}
Jason
fonte
Por favor, nunca faça isso. Modifique apenas o protótipo do que você possui. Isso também não é um polyfill, pois esse método Array não aparece nas especificações do ECMAScript.
Joe Maffei
-2

Enrole suas cordas / /i. Essa é uma maneira fácil de usar o regex para ignorar a caixa

user3225968
fonte
A questão é sobre classificação, não correspondência.
user4642212