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?
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?
Respostas:
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' ]
return a.localeCompare(b, 'en', {'sensitivity': 'base'});
toLowerCase()
quando localeCompare
já 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/…
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.
toLowerCase
duas vezes em cada string; seria mais eficiente armazenar versões reduzidas da string em variáveis.
.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
É 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 sensitivity
opção.
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"]
, 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
.
"33" > "111" === true
e 33 > 111 === false
. Funciona como pretendido.
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"]
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;
});
Normalize o caso .sort()
com .toLowerCase()
.
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).
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 || z
maneira semelhante.
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 null
será classificado entre 'nulk' e 'nulm'. Mas o undefined
será sempre classificado por último.
(''+notdefined) === "undefined"
por isso gostaria classificar antes de "z"
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).
Array.prototype.sort
definiçã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 ignoreCase
retornar 1
ao 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).
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.
undefined
o compareFn nunca
Versão ES6:
["Foo", "bar"].sort((a, b) => a.localeCompare(b, 'en', { sensitivity: 'base' }))
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());
});
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.
***/
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;
}
}
}
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());
});
};
}
Enrole suas cordas / /i
. Essa é uma maneira fácil de usar o regex para ignorar a caixa