Question

J'ai une liste d'objets que je souhaite trier en fonction d'un champ attr de type chaîne.J'ai essayé d'utiliser -

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

mais j'ai trouvé que - ne semble pas fonctionner avec des chaînes en JavaScript.Comment puis-je trier une liste d'objets en fonction d'un attribut de type chaîne ?

Était-ce utile?

La solution

Utiliser String.prototype.localeCompare un selon votre exemple :

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

Nous forçons a.attr à être une chaîne pour éviter les exceptions. localeCompare a été soutenu depuis Internet Explorer 6 et Firefox 1.Vous pouvez également voir le code suivant utilisé qui ne respecte pas les paramètres régionaux :

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;

Autres conseils

Une réponse mise à jour (octobre 2014)

J'étais vraiment ennuyé par cet ordre de tri naturel des chaînes, j'ai donc pris pas mal de temps pour enquêter sur ce problème.J'espère que ça aide.

Longue histoire courte

localeCompare() la prise en charge des personnages est géniale, utilisez-la simplement.Comme l'a souligné Shog9, la réponse à votre question est :

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

Bogues trouvés dans toutes les implémentations javascript personnalisées de « ordre de tri des chaînes naturelles »

Il existe de nombreuses implémentations personnalisées, essayant de faire une comparaison de chaînes plus précisément appelée "ordre de tri naturel des chaînes".

En "jouant" avec ces implémentations, j'ai toujours remarqué un étrange choix "d'ordre de tri naturel", ou plutôt des erreurs (ou des omissions dans le meilleur des cas).

En règle générale, les caractères spéciaux (espace, tiret, esperluette, crochets, etc.) ne sont pas traités correctement.

Vous les trouverez alors mélangés à différents endroits, généralement :

  • certains seront entre le « Z » majuscule et le « a » minuscule
  • certains seront entre le « 9 » et le « A » majuscule
  • certains seront après le « z » minuscule

Alors qu'on aurait pu s'attendre à ce que tous les caractères spéciaux soient "regroupés" en un seul endroit, à l'exception peut-être du caractère spécial espace (qui serait toujours le premier caractère).C'est-à-dire soit tout avant les chiffres, soit tout entre les chiffres et les lettres (les minuscules et les majuscules étant "ensemble" l'une après l'autre), ou tout après les lettres.

Ma conclusion est qu'ils ne parviennent pas tous à fournir un ordre cohérent lorsque je commence à ajouter des caractères à peine inhabituels (c.-à-d.caractères avec des signes diacritiques ou des caractères tels que tiret, point d'exclamation, etc.).

Recherche sur les implémentations personnalisées :

Implémentations natives de « l'ordre de tri des chaînes naturelles » des navigateurs via localeCompare()

localeCompare() l'implémentation la plus ancienne (sans les arguments locales et options) est prise en charge par IE6+, voir http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (faites défiler jusqu'à la méthode localeCompare()).L'intégré localeCompare() La méthode fait un bien meilleur travail de tri, même des caractères internationaux et spéciaux.Le seul problème lors de l'utilisation du localeCompare() la méthode est que "les paramètres régionaux et l'ordre de tri utilisés dépendent entièrement de l'implémentation".En d’autres termes, lors de l’utilisation de localeCompare tel que stringOne.localeCompare(stringTwo) :Firefox, Safari, Chrome et IE ont un ordre de tri différent pour les chaînes.

Recherche sur les implémentations natives du navigateur :

Difficulté de "l'ordre de tri naturel des chaînes"

Implémenter un algorithme solide (c'est-à-dire :cohérent mais couvrant également un large éventail de personnages) est une tâche très difficile.UTF8 contient plus de 2000 caractères & couvre plus de 120 scripts (langues).Enfin, il existe quelques spécifications pour ces tâches, elles sont appelées « Algorithme de classement Unicode », qui peuvent être trouvées sur http://www.unicode.org/reports/tr10/ .Vous pouvez trouver plus d'informations à ce sujet sur cette question que j'ai postée https://softwareengineering.stackexchange.com/questions/257286/is-there-any-langue-agnostic-spécification-for-string-natural-sorting-order

Conclusion finale

Donc, compte tenu du niveau actuel de support fourni par les implémentations personnalisées javascript que j'ai rencontrées, nous ne verrons probablement jamais rien de comparable à la prise en charge de tous ces caractères et scripts (langues).Par conséquent, je préférerais utiliser la méthode localeCompare() native des navigateurs.Oui, il présente l'inconvénient de ne pas être cohérent entre les navigateurs, mais des tests de base montrent qu'il couvre une gamme beaucoup plus large de caractères, permettant des ordres de tri solides et significatifs.

Ainsi, comme l'a souligné Shog9, la réponse à votre question est :

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

Lectures complémentaires :

Merci à la belle réponse de Shog9, qui m'a mis dans la "bonne" direction je crois

Réponse (en ECMAScript moderne)

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

Ou

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

Description

Convertir une valeur booléenne en un nombre donne ce qui suit :

  • true -> 1
  • false -> 0

Considérez trois modèles possibles :

  • x est plus grand que y : (x > y) - (y < x) -> 1 - 0 -> 1
  • x est égal à y : (x > y) - (y < x) -> 0 - 0 -> 0
  • x est plus petit que y : (x > y) - (y < x) -> 0 - 1 -> -1

(Alternative)

  • x est plus grand que y : +(x > y) || -(x < y) -> 1 || 0 -> 1
  • x est égal à y : +(x > y) || -(x < y) -> 0 || 0 -> 0
  • x est plus petit que y : +(x > y) || -(x < y) -> 0 || -1 -> -1

Ces logiques sont donc équivalentes aux fonctions typiques du comparateur de tri.

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

Vous devez utiliser > ou < et == ici.La solution serait donc :

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

Cela me dérangeait depuis longtemps, alors j'ai finalement fait des recherches à ce sujet et je vous ai donné cette longue raison expliquant pourquoi les choses sont telles qu'elles sont.

Du spécification:

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)

Alors maintenant nous passons à 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.

C'est ça. L'opérateur triple égal appliqué aux chaînes renvoie vrai si les arguments sont exactement les mêmes chaînes (même longueur et mêmes caractères aux positions correspondantes).

Donc === fonctionnera dans les cas où nous essayons de comparer des chaînes qui pourraient provenir de différentes sources, mais dont nous savons qu'elles finiront par avoir les mêmes valeurs - un scénario assez courant pour les chaînes en ligne dans notre code.Par exemple, si nous avons une variable nommée connection_state, et nous souhaitons savoir lequel des états suivants ['connecting', 'connected', 'disconnecting', 'disconnected'] est-ce en ce moment, on peut directement utiliser le ===.

Mais il y a plus.Juste au-dessus de la version 11.9.4, il y a une courte note :

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.Et maintenant?Les chaînes obtenues de l'extérieur peuvent, et seront très probablement, être d'étranges Unicodey, et notre doux === ne leur rendra pas justice.Arrive localeCompare à la rescousse:

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. 

Nous pouvons rentrer à la maison maintenant.

tl;dr;

Pour comparer des chaînes en javascript, utilisez localeCompare;si vous savez que les chaînes n'ont pas de composants non-ASCII car ce sont, par exemple, des constantes internes du programme, alors === fonctionne aussi.

Fonction de flèche ternaire imbriquée

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

Dans votre opération dans votre question initiale, vous effectuez l'opération suivante :

item1.attr - item2.attr

Donc, en supposant que ce soient des chiffres (c'est-à-direitem1.attr = "1", item2.attr = "2") Vous pouvez toujours utiliser l'opérateur "===" (ou d'autres évaluateurs stricts) à condition de garantir le type.Les éléments suivants devraient fonctionner :

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

S'ils sont alphanumériques, utilisez localCompare().

list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

Comment ils fonctionnent des échantillons :

+('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)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top