Pregunta

Tengo una lista de objetos que deseo ordenar según un campo attr de tipo cadena.Intenté 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 según un atributo con tipo cadena?

¿Fue útil?

Solución

Usar String.prototype.localeCompare a según tu 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 apoyado desde Internet Explorer 6 y Firefox 1.También es posible que veas 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;

Otros consejos

Una respuesta actualizada (octubre de 2014)

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

Larga historia corta

localeCompare() El soporte de personajes es genial, solo úsalo.Como lo señala Shog9, la respuesta a tu pregunta es:

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

Errores encontrados en todas las implementaciones personalizadas de "orden de clasificación de cadenas naturales" de JavaScript

Existen muchas implementaciones personalizadas que intentan realizar una comparación de cadenas con mayor precisión, lo que se denomina "orden de clasificación de cadenas natural".

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

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

Luego los encontrarás mezclados en diferentes lugares, normalmente podrían ser:

  • 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 quizás el carácter especial de espacio (que siempre sería el primer carácter).Es decir, todo antes de los números, o todo entre números y letras (las minúsculas y mayúsculas están "juntas" una tras otra), o todo después de las letras.

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

Investigación sobre las implementaciones personalizadas:

Implementaciones nativas de "orden de clasificación de cadenas naturales" de los navegadores a través de localeCompare()

localeCompare() La implementación más antigua (sin los argumentos locales y de opciones) es compatible con IE6+, consulte http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (Desplácese hacia abajo hasta el método localeCompare()).el incorporado localeCompare() El método hace un trabajo mucho mejor al clasificar, incluso caracteres internacionales y especiales.El único problema al utilizar el localeCompare() El método es ese "La configuración regional y el orden de clasificación utilizados dependen totalmente de la implementación".En otras palabras, cuando se utiliza 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 cadenas"

Implementar un algoritmo sólido (es decir:consistente pero también cubriendo una amplia gama de personajes) es una tarea muy difícil.UTF8 contiene más de 2000 caracteres & cubre más de 120 guiones (idiomas).Finalmente, existe una especificación para esta tarea, se llama "Algoritmo de intercalación Unicode", que se puede encontrar en http://www.unicode.org/reports/tr10/ .Puedes 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

Entonces, considerando el nivel actual de soporte brindado por las implementaciones personalizadas de JavaScript que encontré, probablemente nunca veremos nada que se acerque a admitir todos estos caracteres y scripts (idiomas).Por lo tanto, prefiero utilizar el método localeCompare() nativo de los navegadores.Sí, tiene la desventaja de no ser consistente en todos los navegadores, pero las pruebas básicas muestran que cubre una gama mucho más amplia de caracteres, lo que permite un orden de clasificación sólido y significativo.

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

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

Otras lecturas:

Gracias a la agradable respuesta de Shog9, que creo que me puso en la dirección "correcta".

Respuesta (en ECMAScript moderno)

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))

Descripción

Al convertir un valor booleano a un número se obtiene lo siguiente:

  • true -> 1
  • false -> 0

Considere tres patrones posibles:

  • x es mayor que y: (x > y) - (y < x) -> 1 - 0 -> 1
  • x es igual a y: (x > y) - (y < x) -> 0 - 0 -> 0
  • x es menor que y: (x > y) - (y < x) -> 0 - 1 -> -1

(Alternativa)

  • x es mayor que y: +(x > y) || -(x < y) -> 1 || 0 -> 1
  • x es igual a y: +(x > y) || -(x < y) -> 0 || 0 -> 0
  • x es menor que y: +(x > y) || -(x < y) -> 0 || -1 -> -1

Entonces, estas lógicas son equivalentes a las funciones típicas de comparación de clasificación.

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;

Deberías usar > o < y == aquí.Entonces la solución sería:

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

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

Desde el Especificaciones:

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)

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 verdadero si los argumentos son exactamente las mismas cadenas (misma longitud y mismos caracteres en las posiciones correspondientes).

Entonces === funcionará en los casos en los que intentemos comparar cadenas que podrían haber llegado de diferentes fuentes, pero que sabemos que eventualmente tendrán los mismos valores, un escenario bastante común para cadenas en línea en nuestro código.Por ejemplo, si tenemos una variable llamada connection_state, y deseamos saber cuál de los siguientes estados ['connecting', 'connected', 'disconnecting', 'disconnected'] ¿Está en este momento? Podemos usar directamente el ===.

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

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.

Mmm.¿Ahora que?Las cadenas obtenidas externamente pueden ser unicodey extrañas, y lo más probable es que lo sean, y nuestro amable === no les hará justicia.Entra 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 que no sean ASCII porque son, por ejemplo, constantes internas del programa, entonces === también funciona.

Función de flecha ternaria anidada

(a,b) => (a < b ? -1 : a > b ? 1 : 0)

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

item1.attr - item2.attr

Entonces, suponiendo que sean números (es decir,item1.attr = "1", item2.attr = "2") Aún puede utilizar el operador "===" (u otros evaluadores estrictos) siempre que garantice el tipo.Lo siguiente debería funcionar:

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

Si son alfanuméricos, utilice localCompare().

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
<!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>
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top