¿Cómo realizar una ordenación sin distinción entre mayúsculas y minúsculas en JavaScript?

220

Tengo una serie de cadenas que necesito ordenar en JavaScript, pero sin distinción entre mayúsculas y minúsculas. ¿Cómo realizar esto?

Jérôme Verstrynge
fuente

Respuestas:

404

En (casi :) una línea

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

Lo que resulta en

[ 'bar', 'Foo' ]

Mientras

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

resultados en

[ 'Foo', 'bar' ]
Ivan Krechetov
fuente
99
Tenga en cuenta que las opciones avanzadas de localeCompare aún no son compatibles con todas las plataformas / navegadores. Sé que no se usan en este ejemplo, pero solo quería agregar para mayor claridad. Ver MDN para más información
Ayame__
97
Si va a involucrar localeCompare (), podría usar su capacidad para no distinguir entre mayúsculas y minúsculas, por ejemplo:return a.localeCompare(b, 'en', {'sensitivity': 'base'});
Michael Dyck
2
+1 por no llamar toLowerCase()cuando localeCompareya lo hace de forma predeterminada en algunos casos. Puede leer más sobre los parámetros para pasar aquí: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Milimetric
3
@Milimetric de acuerdo con la página referenciada, esa característica no es compatible con algunos navegadores (por ejemplo, IE <11 o Safari). La solución mencionada aquí es muy buena, pero aún requeriría backporting / polyfill para algunos navegadores.
3k-
2
Si tiene una gran variedad, tiene sentido usarla items.sort(new Intl.Collator('en').compare)para un mejor rendimiento. (Ver MDN .)
valtlai
60
myArray.sort(
  function(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;
  }
);

EDITAR: Tenga en cuenta que originalmente escribí esto para ilustrar la técnica en lugar de tener en cuenta el rendimiento. Consulte también la respuesta @Ivan Krechetov para obtener una solución más compacta.

ron tornambe
fuente
3
Esto puede llamar toLowerCasedos veces en cada cadena; Sería más eficiente almacenar versiones reducidas de la cadena en variables.
Jacob
Cierto y gracias. Escribí esto con claridad en mente, no con rendimiento. Supongo que debería tener en cuenta eso.
ron tornambe
1
@Jacob Para ser justos, la respuesta aceptada tiene el mismo problema básico: posiblemente puede llamar .toLowerCase()varias veces para cada elemento del conjunto. Por ejemplo, 45 llamadas a la función de comparación al ordenar 10 elementos en orden inverso. 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
nada es necesario
47

Es hora de revisar esta vieja pregunta.

No debe usar soluciones confiables toLowerCase. Son ineficientes y simplemente no funcionan en algunos idiomas (turco, por ejemplo). Prefiero esto:

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

Consulte la documentación de compatibilidad del navegador y todo lo que hay que saber sobre la sensitivityopción.

ZunTzu
fuente
1
Tenga cuidado, esto no es compatible con todos los motores de 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 the Dark Absol
fuente
1
oreturn a === b ? 0 : a > b ? 1 : -1;
Devin G Rhode
Es probable que esto no funcione según lo previsto para las cadenas que representan números. Los operadores aritméticos usarán la semántica de números en lugar de cadenas. Por ejemplo, si lo hemos hecho ["111", "33"], podríamos querer que vuelva ["111", "33"]porque 1 viene antes que 3 en el orden del código de caracteres. Sin embargo, la función en esta respuesta volverá ["33", "111"]porque el número 33es menor que el número 111.
Austin Davis
@AustinDavis "33" > "111" === truey 33 > 111 === false. Funciona según lo previsto.
Niet the Dark Absol
12

También puede usar el nuevo Intl.Collator().compare, según MDN, es más eficiente al ordenar matrices. La desventaja es que no es compatible con navegadores antiguos. MDN afirma que no es compatible en absoluto en Safari. Es necesario verificarlo, ya que indica que Intl.Collatores compatible.

Al comparar grandes cantidades de cadenas, como al ordenar grandes matrices, es mejor crear un objeto Intl.Collator y usar la función proporcionada por su propiedad de comparación

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

Si desea garantizar el mismo orden independientemente del orden de los elementos en la matriz de entrada, aquí hay una clasificación estable :

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
fuente
5

Normalizar el caso en el .sort()con .toLowerCase().


fuente
4

También puede usar el operador de 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);

Da:

biscuit,Bob,charley,fudge,Fudge

Sin embargo, el método localeCompare probablemente esté bien ...

Nota: El operador de Elvis es una forma abreviada de 'operador ternario' para si, de lo contrario, generalmente con asignación.
Si miras el?: De lado, se parece a Elvis ...
es decir, en lugar de:

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

puedes usar:

x = y?1:2;

es decir, cuando y es verdadero, luego devuelve 1 (para la asignación a x), de lo contrario devuelve 2 (para la asignación a x).

AndyS
fuente
55
Para ser pedante, este no es el operador de Elvis. Esto es solo un operador ternario básico. Un verdadero operador de Elvis es nulo-coalescente, por ejemplo, en lugar de x = y ? y : z, puede hacerlo x = y ?: z. Javascript no tiene un operador real de Elvis, pero puede usarlo x = y || zde manera similar.
Charles Wood
3

Las otras respuestas suponen que la matriz contiene cadenas. Mi método es mejor, porque funcionará incluso si la matriz contiene nulos, indefinidos u otras cadenas.

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

El nullse ordenará entre 'nulk' y 'nulm'. Sin embargo, el undefinedserá siempre ordenados pasado.

John Henckel
fuente
(''+notdefined) === "undefined"así que se
ordenaría
Supongo que debería haber buscado la definición de Array.prototype.sort: | porque la parte acerca de (''+notdefined) === "undefined" realmente es verdadera ... lo que significa que si volteas el -1 y el 1 en la función de clasificación para revertir el orden, undefined aún se ordena hasta el final. También debe tenerse en cuenta al usar la función de comparación fuera del contexto de una ordenación de matriz (como lo hice cuando me encontré con esta pregunta).
MattW
Y después de haber reflexionado sobre esa Array.prototype.sortdefinición, un par de comentarios más. Primero, no hay necesidad de (''+a)- ECMAScript requiere toString()que se llame a elementos antes de pasarlos a compareFn. En segundo lugar, el hecho de que ignoreCaseregrese 1al comparar cadenas iguales (incluidas las cadenas iguales pero para el caso) significa que la especificación no define el resultado si hay valores duplicados (creo que probablemente estará bien solo con algunos intercambios innecesarios, creo).
MattW
@MattW, me parece que undefinedes un caso especial, que para cualquier x x <undefined y x> undefined son falsos . Eso undefinedsiempre es el último, es un subproducto de la implementación del tipo de clasificación. Intenté cambiar el ('' + a) a simplemente a, pero falla. me sale TypeError: a.toUpperCase is not a function. Al parecer toStringse no llama antes de llamar compareFn.
John Henckel
1
Ah, está bien, eso tiene mucho sentido. Para undefinedel compareFn nunca se llama
John Henckel
1

En apoyo de la respuesta aceptada, me gustaría agregar que la función a continuación parece cambiar los valores en la matriz original que se ordenará, de modo que no solo clasificará en minúsculas sino que los valores en mayúsculas también se cambiarán a minúsculas. Esto es un problema para mí porque, aunque deseo ver a Mary junto a Mary, no deseo que el caso del primer valor Mary se cambie a minúsculas.

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

En mis experimentos, la siguiente función de la respuesta aceptada se ordena correctamente pero no cambia los valores.

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

Esto puede ayudar si le ha costado 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
fuente
0
arr.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if( a == b) return 0;
    if( a > b) return 1;
    return -1;
});

En la función anterior, si solo comparamos cuando dos minúsculas valoran a y b, no tendremos el resultado bonito.

Ejemplo, si la matriz es [A, a, B, b, c, C, D, d, e, E] y usamos la función anterior, tenemos exactamente esa matriz. No ha cambiado nada.

Para obtener el resultado es [A, a, B, b, C, c, D, d, E, e], debemos comparar nuevamente cuando dos valores en minúscula son iguales:

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;
        }
    }
}
Envidia
fuente
-1

Envolví la respuesta superior en un polyfill para poder llamar a .sortIgnoreCase () en matrices de cadenas

// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
    Array.prototype.sortIgnoreCase = function () {
        return this.sort(function (a, b) {
            return a.toLowerCase().localeCompare(b.toLowerCase());
        });
    };
}
Jason
fuente
Por favor, nunca hagas esto. Solo modifique el prototipo de las cosas que posee. Esto tampoco es un polyfill, ya que este método de matriz no está en ninguna parte de las especificaciones ECMAScript.
Joe Maffei
-2

Envuelve tus cuerdas / /i. Esta es una manera fácil de usar expresiones regulares para ignorar la carcasa

usuario3225968
fuente
La pregunta es sobre la clasificación, no la coincidencia.
user4642212