2010-05-10 12 views
69

Je suis à la recherche de la façon la plus simple de trier un tableau composé de chiffres et de texte, et une combinaison de ceux-ci.Javascript: type naturel de chaînes alphanumériques

E.g.

'123asd' 
'19asd' 
'12345asd' 
'asd123' 
'asd12' 

se transforme en

'19asd' 
'123asd' 
'12345asd' 
'asd12' 
'asd123' 

Cela va être utilisé en combinaison avec la solution à another question I've asked here.

La fonction de tri fonctionne en soi, ce dont j'ai besoin est une fonction qui peut dire que '19asd' est plus petit que '123asd'.

J'écris ceci en JavaScript.

Edit: comme adormitu a fait remarquer, ce que je suis à la recherche est une fonction de tri

+0

Voir aussi 'Comment faites-vous la comparaison de chaînes en JavaScript' sur http://stackoverflow.com/questions/51165?/how-do-you-do-string-comparison-in-javascript –

+1

La question originale a été posée en 2010, donc ce ne serait pas surprenant :) – ptrn

Répondre

102

Ceci est désormais possible dans les navigateurs modernes en utilisant localeCompare. En passant l'option numeric: true, il reconnaîtra intelligemment les nombres. Vous pouvez ignorer la casse en utilisant sensitivity: 'base'. Testé dans Chrome, Firefox et IE11.

Voici un exemple. Il retourne 1, ce qui signifie 10 va après 2:

'10'.localeCompare('2', undefined, {numeric: true, sensitivity: 'base'})

Pour des performances lors du tri grand nombre de chaînes, l'article dit:

Lorsque l'on compare un grand nombre de chaînes, comme dans le tri grand arrays, il est préférable de créer un objet Intl.Collator et d'utiliser la fonction fournie par sa propriété compare.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare

var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'}); 
var myArray = ['1_Document', '11_Document', '2_Document']; 
myArray.sort(collator.compare); 
// myArray is now ['1_Document', '2_Document', '11_Document'] 
+8

upvote pour un one-liner moderne +1! – A1rPun

+1

Génial, belle approche –

+1

Génial, Vous avez résolu mon problème. Merci –

38

naturel Vous avez donc besoin d'un tri naturel?

Si oui, peut-être que this script by Brian Huisman based on David koelle's work serait ce dont vous avez besoin.

Il semble que la solution de Brian Huisman est maintenant hébergé directement sur le blog de David Koelle:

+0

Un tri correct et naturel est ce que je cherche. Je vais regarder dans le lien que vous avez envoyé, merci – ptrn

+0

Cela a certainement fait l'affaire. Merci beaucoup! – ptrn

+0

C'est un genre très artificiel. Il ne produit pas un tri alphbetic. – tchrist

17

Pour comparer les valeurs que vous pouvez utiliser une comparaison de méthodolo-

function naturalSorter(as, bs){ 
    var a, b, a1, b1, i= 0, n, L, 
    rx=/(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.\D+)|(\.$)/g; 
    if(as=== bs) return 0; 
    a= as.toLowerCase().match(rx); 
    b= bs.toLowerCase().match(rx); 
    L= a.length; 
    while(i<L){ 
     if(!b[i]) return 1; 
     a1= a[i], 
     b1= b[i++]; 
     if(a1!== b1){ 
      n= a1-b1; 
      if(!isNaN(n)) return n; 
      return a1>b1? 1:-1; 
     } 
    } 
    return b[i]? -1:0; 
} 

Mais pour la vitesse en sorti ng un tableau, gréer le tableau avant le tri, de sorte que vous avez seulement à faire des conversions minuscules et l'expression régulière une fois au lieu de dans chaque étape à travers le tri.

function naturalSort(ar, index){ 
    var L= ar.length, i, who, next, 
    isi= typeof index== 'number', 
    rx= /(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.(\D+|$))/g; 
    function nSort(aa, bb){ 
     var a= aa[0], b= bb[0], a1, b1, i= 0, n, L= a.length; 
     while(i<L){ 
      if(!b[i]) return 1; 
      a1= a[i]; 
      b1= b[i++]; 
      if(a1!== b1){ 
       n= a1-b1; 
       if(!isNaN(n)) return n; 
       return a1>b1? 1: -1; 
      } 
     } 
     return b[i]!= undefined? -1: 0; 
    } 
    for(i= 0; i<L; i++){ 
     who= ar[i]; 
     next= isi? ar[i][index] || '': who; 
     ar[i]= [String(next).toLowerCase().match(rx), who]; 
    } 
    ar.sort(nSort); 
    for(i= 0; i<L; i++){ 
     ar[i]= ar[i][1]; 
    } 
} 
+0

cela fonctionnerait-il dans mon cas, avec le tableau intérieur décidant de l'ordre de l'externe? – ptrn

+0

Qu'est-ce que 'String.prototype.tlc()'? Est-ce votre propre code ou l'avez-vous obtenu quelque part? Si ce dernier, s'il vous plaît lien vers la page. –

+0

désolé pour l'erreur corrigée, merci. Si vous voulez que [1] et b [1] contrôlent le tri, utilisez a = String (a [1]). ToLowerCase(); b = Chaîne (b [1]).toLowerCase(); – kennebec

0

Miser sur @Adrien Soyez Répondons ci-dessus et en utilisant le code qui Brian Huisman & David koelle créé, voici un prototype modifié de tri pour un tableau d'objets:

//Usage: unsortedArrayOfObjects.alphaNumObjectSort("name"); 
//Test Case: var unsortedArrayOfObjects = [{name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a10"}, {name: "a5"}, {name: "a13"}, {name: "a20"}, {name: "a8"}, {name: "8b7uaf5q11"}]; 
//Sorted: [{name: "8b7uaf5q11"}, {name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a5"}, {name: "a8"}, {name: "a10"}, {name: "a13"}, {name: "a20"}] 

// **Sorts in place** 
Array.prototype.alphaNumObjectSort = function(attribute, caseInsensitive) { 
    for (var z = 0, t; t = this[z]; z++) { 
    this[z].sortArray = new Array(); 
    var x = 0, y = -1, n = 0, i, j; 

    while (i = (j = t[attribute].charAt(x++)).charCodeAt(0)) { 
     var m = (i == 46 || (i >=48 && i <= 57)); 
     if (m !== n) { 
     this[z].sortArray[++y] = ""; 
     n = m; 
     } 
     this[z].sortArray[y] += j; 
    } 
    } 

    this.sort(function(a, b) { 
    for (var x = 0, aa, bb; (aa = a.sortArray[x]) && (bb = b.sortArray[x]); x++) { 
     if (caseInsensitive) { 
     aa = aa.toLowerCase(); 
     bb = bb.toLowerCase(); 
     } 
     if (aa !== bb) { 
     var c = Number(aa), d = Number(bb); 
     if (c == aa && d == bb) { 
      return c - d; 
     } else { 
      return (aa > bb) ? 1 : -1; 
     } 
     } 
    } 

    return a.sortArray.length - b.sortArray.length; 
    }); 

    for (var z = 0; z < this.length; z++) { 
    // Here we're deleting the unused "sortArray" instead of joining the string parts 
    delete this[z]["sortArray"]; 
    } 
} 
0

Imaging une fonction de remplissage à 8 chiffres, une qui transformerait '123asd' et '19asd' en '00000123asd' et '00000019asd' respectivement. L'utilisation de ces versions de chaînes nous aidera à trier "19asd" avant "123asd".

Nous pouvons utiliser l'expression régulière /\d+/g pour aider à trouver tous les numéros qui doivent être rembourré:

str.replace(/\d+/g, pad) 

Le tableau suivant montre le tri en utilisant cette technique:

var list = [ 
 
    '123asd', 
 
    '19asd', 
 
    '12345asd', 
 
    'asd123', 
 
    'asd12' 
 
]; 
 

 
function pad(n) { return ("00000000" + n).substr(-8); } 
 
function natural_expand(a) { return a.replace(/\d+/g, pad) }; 
 
function natural_compare(a, b) { 
 
    return natural_expand(a).localeCompare(natural_expand(b)); 
 
} 
 

 
console.log(list.map(natural_expand).sort()); // intermediate values 
 
console.log(list.sort(natural_compare)); // result

Les résultats intermédiaires montrent ce que la routine natural_expand() fait et vous donne un comprendre comment la routine natural_compare suivante fonctionnera:

[ 
    "00000019asd", 
    "00000123asd", 
    "00asd", 
    "asd00000012", 
    "asd00000123" 
] 

Sorties:

[ 
    "19asd", 
    "123asd", 
    "12345asd", 
    "asd12", 
    "asd123" 
]