Cómo ordenar cadenas en JavaScript


Tengo una lista de objetos que deseo ordenar basada en un campo attr de tipo string. He intentado usar -

list.sort(function (a, b) {
    return a.attr - b.attr
})

Pero encontró que - no parece funcionar con cadenas en JavaScript. ¿Cómo puedo ordenar una lista de objetos basada en un atributo con el tipo string?

Author: royhowie, 2008-09-09

8 answers

Uso String.prototype.localeCompare a por su ejemplo:

list.sort(function (a, b) {
    return ('' + a.attr).localeCompare(b.attr);
})

Forzamos a.attr a ser una cadena para evitar excepciones. localeCompare ha sido soportado desde Internet Explorer 6 y Firefox 1. También puede ver el siguiente código utilizado que no respeta una configuración regional:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;
 392
Author: Shog9,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-28 22:35:45

Una respuesta actualizada (octubre de 2014)

Estaba realmente molesto por este orden de clasificación natural de cadenas, así que me tomé bastante tiempo para investigar este problema. Espero que esto ayude.

Larga historia corta

localeCompare() el soporte de personajes es rudo, solo úsalo. Como señaló Shog9, la respuesta a su pregunta es:

return item1.attr.localeCompare(item2.attr);

Errores encontrados en todas las implementaciones personalizadas de javascript "natural string sort order"

Hay un montón de costumbre implementaciones por ahí, tratando de hacer la comparación de cadenas más precisamente llamado "orden natural de clasificación de cadenas"

Al "jugar" con estas implementaciones, siempre noté alguna extraña elección de "orden natural de clasificación", o más bien errores (u omisiones en el mejor de los casos).

Normalmente, los caracteres especiales (espacio, guión, ampersand, corchetes, etc.) no se procesan correctamente.

Luego los encontrará apareciendo mezclados en diferentes lugares, típicamente eso podría be:

  • algunos estarán entre la 'Z' mayúscula y la 'a' minúscula
  • algunos estarán entre el ' 9 'y la'A' mayúscula
  • algunos estarán después de la 'z'minúscula

Cuando uno hubiera esperado que todos los caracteres especiales estuvieran "agrupados" en un solo lugar, excepto el carácter especial del espacio tal vez (que siempre sería el primer carácter). Es decir, todo antes de números, o todo entre números y letras (minúsculas y mayúsculas siendo "juntos" uno tras otro), o todos después de las letras.

Mi conclusión es que todos ellos no proporcionan un orden consistente cuando empiezo a agregar caracteres apenas inusuales (es decir. caracteres con diacríticos o caracteres como guión, signo de exclamación, etc.).

Investigación sobre las implementaciones personalizadas:

Implementaciones nativas de "orden natural de clasificación de cadenas" de los navegadores via localeCompare()

localeCompare() la implementación más antigua (sin los argumentos locales y options) es soportada por IE6+, ver http://msdn.microsoft.com/en-us/library/ie/s4esdbwz (v=vs.94). aspx (desplácese hacia abajo hasta el método localeCompare ()). El método incorporado localeCompare() hace un trabajo mucho mejor a la hora de ordenar, incluso caracteres internacionales y especiales. El único problema con el método localeCompare() es que "la configuración regional y el orden de ordenación utilizados dependen completamente de la implementación". En otras palabras, al usar localeCompare como stringOne.localeCompare (stringTwo): Firefox, Safari, Chrome e IE tienen un orden de clasificación diferente para las cadenas.

Investigación sobre las implementaciones nativas del navegador:

Dificultad del "orden de clasificación natural de la cadena"

Implementando un algoritmo sólido (significado: consistente pero también cubre una amplia gama de personajes) es una tarea muy difícil. UTF8 contiene más de 2000 caracteres & cubre más de 120 scripts (idiomas) . Finalmente, hay algunas especificaciones para estas tareas, se llama el "Algoritmo de Intercalación Unicode", que se puede encontrar en http://www.unicode.org/reports/tr10 / . Puede encontrar más información sobre esto en esta pregunta que publiqué https://softwareengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

Conclusión final

Así que teniendo en cuenta el nivel actual de soporte proporcionado por las implementaciones personalizadas de javascript que encontré, probablemente nunca veremos nada que se acerque a soportar todos estos caracteres y scripts (idiomas). Por lo tanto, prefiero usar el método nativo localeCompare() de los navegadores. Sí, es tiene la desventaja de ser no consistente en todos los navegadores, pero las pruebas básicas muestran que cubre una gama mucho más amplia de caracteres, lo que permite órdenes de clasificación sólidas y significativas.

Así como lo señala Shog9, la respuesta a su pregunta es:

return item1.attr.localeCompare(item2.attr);

Lectura adicional:

Gracias a la buena respuesta de Shog9, que me puso en el " derecho" dirección creo

 115
Author: Adrien Be,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:07

Respuesta más simple con ECMAScript 2016

list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))

O

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))
 12
Author: mpyw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-07 03:59:03

Debe usar > o

list.sort(function(item1, item2) {
    var val1 = item1.attr,
        val2 = item2.attr;
    if (val1 == val2) return 0;
    if (val1 > val2) return 1;
    if (val1 < val2) return -1;
});
 7
Author: airportyh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-03 18:04:21

Me había preocupado por esto durante mucho tiempo, así que finalmente investigué esto y les di esta larga razón de por qué las cosas son como son.

De la especificación :

Section 11.9.4   The Strict Equals Operator ( === )

The production EqualityExpression : EqualityExpression === RelationalExpression
is evaluated as follows: 
- Let lref be the result of evaluating EqualityExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating RelationalExpression.
- Let rval be GetValue(rref).
- Return the result of performing the strict equality comparison 
  rval === lval. (See 11.9.6)

Así que ahora vamos a 11.9.6

11.9.6   The Strict Equality Comparison Algorithm

The comparison x === y, where x and y are values, produces true or false. 
Such a comparison is performed as follows: 
- If Type(x) is different from Type(y), return false.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is Number, then
...
- If Type(x) is String, then return true if x and y are exactly the 
  same sequence of characters (same length and same characters in 
  corresponding positions); otherwise, return false.

Eso es todo. El operador triple igual aplicado a cadenas devuelve true iff los argumentos son exactamente las mismas cadenas (la misma longitud y los mismos caracteres en las posiciones correspondientes).

Así que === funcionará en los casos en que estamos tratando de comparar cadenas que podrían haber llegado de diferentes fuentes, pero que sabemos que eventualmente tendrán los mismos valores, un escenario lo suficientemente común para cadenas en línea en nuestro código. Por ejemplo, si tenemos una variable llamada connection_state, y queremos saber en cuál de los siguientes estados ['connecting', 'connected', 'disconnecting', 'disconnected'] se encuentra en este momento, podemos usar directamente el ===.

Pero hay más. Justo por encima de 11.9.4, hay una nota corta:

NOTE 4     
  Comparison of Strings uses a simple equality test on sequences of code 
  unit values. There is no attempt to use the more complex, semantically oriented
  definitions of character or string equality and collating order defined in the 
  Unicode specification. Therefore Strings values that are canonically equal
  according to the Unicode standard could test as unequal. In effect this 
  algorithm assumes that both Strings are already in normalized form.

Hmm. ¿Ahora qué? Las cadenas obtenidas externamente pueden, y lo más probable es que sea raro unicodey, y nuestro gentil === no les hará justicia. En viene localeCompare al rescate:

15.5.4.9   String.prototype.localeCompare (that)
    ...
    The actual return values are implementation-defined to permit implementers 
    to encode additional information in the value, but the function is required 
    to define a total ordering on all Strings and to return 0 when comparing
    Strings that are considered canonically equivalent by the Unicode standard. 

Podemos irnos a casa ahora.

Tl;dr;

Para comparar cadenas en javascript, use localeCompare; si sabe que las cadenas no tienen componentes no ASCII porque son, por ejemplo, constantes internas del programa, entonces === también funciona.

 4
Author: Manav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-07 18:06:20
<!doctype html>
<html>
<body>
<p id = "myString">zyxtspqnmdba</p>
<p id = "orderedString"></p>
<script>
var myString = document.getElementById("myString").innerHTML;
orderString(myString);
function orderString(str) {
    var i = 0;
    var myArray = str.split("");
    while (i < str.length){
        var j = i + 1;
        while (j < str.length) {
            if (myArray[j] < myArray[i]){
                var temp = myArray[i];
                myArray[i] = myArray[j];
                myArray[j] = temp;
            }
            j++;
        }
        i++;
    }
    var newString = myArray.join("");
    document.getElementById("orderedString").innerHTML = newString;
}
</script>
</body>
</html>
 1
Author: Julio Munoz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-23 20:45:28

En su operación en su pregunta inicial, está realizando la siguiente operación:

item1.attr - item2.attr

Entonces, asumiendo que son números (es decir, item1.attr = "1", item2.attr = "2") Todavía puede usar el operador "= = = " (u otros evaluadores estrictos) siempre que se asegure de tipo. Lo siguiente debería funcionar:

return parseInt(item1.attr) - parseInt(item2.attr);

Si son alfanuméricos, entonces use localCompare().

 0
Author: eggmatters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-24 18:15:33
list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

Cómo funcionan las muestras:

+('aaa'>'bbb')||+('aaa'==='bbb')-1
+(false)||+(false)-1
0||0-1
-1

+('bbb'>'aaa')||+('bbb'==='aaa')-1
+(true)||+(false)-1
1||0-1
1

+('aaa'>'aaa')||+('aaa'==='aaa')-1
+(false)||+(true)-1
0||1-1
0
 0
Author: Petr Varyagin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-07 11:43:34