2011-11-12 19 views
8

Vorrei ordinare una serie di stringhe (in javascript) in modo che i gruppi di cifre all'interno delle stringhe vengano confrontati come numeri interi e non come stringhe. Non sono preoccupato per i numeri firmati o in virgola mobile.come ordinare le stringhe in javascript numericamente

per esempio, il risultato dovrebbe essere ["a1b3","a9b2","a10b2","a10b11"] non ["a1b3","a10b11","a10b2","a9b2"]

Il modo più semplice per farlo sembra essere suddividendo ciascuna stringa su confini intorno gruppi di cifre. C'è un pattern che posso passare a String.split per dividere i limiti dei caratteri senza rimuovere alcun carattere?

"abc11def22ghi".split(/?/) = ["abc","11","def","22","ghi"];

O c'è un altro modo per confrontare le stringhe che non comporta dividendoli, forse da imbottitura tutti i gruppi di cifre con zeri iniziali in modo che siano della stessa lunghezza?

"aa1bb" => "aa00000001bb", "aa10bb" => "aa00000010bb"

Sto lavorando con stringhe arbitrarie, non stringhe che hanno un accordo specifico di gruppi di cifre.

Edit:

Mi piace il /(\d+)/ uno di linea da Gaby per dividere l'array. Quanto è retrocompatibile?

Le soluzioni che analizzano le stringhe in un modo che può essere utilizzato per ricostruire gli originali sono molto più efficienti di questa funzione di confronto. Nessuna delle risposte gestisce alcune stringhe che iniziano con cifre e altre no, ma ciò sarebbe abbastanza facile da porre rimedio e non era esplicito nella domanda originale.

["a100","a20","a3","a3b","a3b100","a3b20","a3b3","!!","~~","9","10","9.5"].sort(function (inA , inB) { 
    var      result = 0; 

    var      a , b , pattern = /(\d+)/; 
    var      as = inA.split(pattern); 
    var      bs = inB.split(pattern); 
    var      index , count = as.length; 

    if (('' === as[0]) === ('' === bs[0])) { 
     if (count > bs.length) count = bs.length; 

     for (index = 0 ; index < count && 0 === result ; ++index) { 
      a = as[index]; b = bs[index]; 

      if (index & 1) { 
       result = a - b; 
      } else { 
       result = !(a < b) ? (a > b) ? 1 : 0 : -1; 
      } 
     } 

     if (0 === result) result = as.length - bs.length; 
    } else { 
     result = !(inA < inB) ? (inA > inB) ? 1 : 0 : -1; 
    } 

    return result; 
}).toString(); 

risultato: "!!,9,9.5,10,a3,a3b,a3b3,a3b20,a3b100,a20,a100,~~"

+0

Le parti non numeriche sono sempre le stesse? In caso contrario, l'algoritmo di ordinamento dovrebbe ordinarli in ordine ASCII? –

+1

Nel tuo esempio, stai estraendo 13, 92, 102, 1011? O è più simile a 1.3, 9.2, 10.2, 10.11? Voglio dire, il primo numero è più significativo o le lettere sono semplicemente ignorate? –

+0

... oh, vuoi ancora ordinare i non interi anche io, lo capisco adesso ... –

risposta

9

penso che questo fa ciò che si vuole

function sortArray(arr) { 
    var tempArr = [], n; 
    for (var i in arr) { 
     tempArr[i] = arr[i].match(/([^0-9]+)|([0-9]+)/g); 
     for (var j in tempArr[i]) { 
      if(! isNaN(n = parseInt(tempArr[i][j]))){ 
       tempArr[i][j] = n; 
      } 
     } 
    } 
    tempArr.sort(function (x, y) { 
     for (var i in x) { 
      if (y.length < i || x[i] < y[i]) { 
       return -1; // x is longer 
      } 
      if (x[i] > y[i]) { 
       return 1; 
      } 
     } 
     return 0; 
    }); 
    for (var i in tempArr) { 
     arr[i] = tempArr[i].join(''); 
    } 
    return arr; 
} 
alert(
    sortArray(["a1b3", "a10b11", "a10b2", "a9b2"]).join(",") 
); 
+11

Funziona con stacksort. –

5

Supponendo che cosa si vuole fare è solo che un ordinamento numerico dalle cifre in ogni voce array (ignorando la non-cifre), è possibile utilizzare questo:

function sortByDigits(array) { 
    var re = /\D/g; 

    array.sort(function(a, b) { 
     return(parseInt(a.replace(re, ""), 10) - parseInt(b.replace(re, ""), 10)); 
    }); 
    return(array); 
} 

Utilizza una funzione di ordinamento personalizzata che rimuove le cifre e converte in un numero ogni volta che viene chiesto di effettuare un confronto. Potete vederlo funzionare qui: http://jsfiddle.net/jfriend00/t87m2/.

Se questo non è ciò che si desidera, quindi si prega di chiarire come la tua domanda non è molto chiara su hwo l'ordinamento dovrebbe effettivamente funzionare.

+0

Penso che ci potrebbero essere problemi se incontra un numero di piombo pari a zero, no? vale a dire. abc03def45 –

+0

@ Dr.Dredel - L'uso di parseInt lo rende un puro ordinamento numerico. Gli zeri iniziali verranno ignorati quando convertiti in un numero vero come dovrebbero. Non vedo alcun problema – jfriend00

+0

ah ... molto vero ... nm –

1

Here's a more complete solution che ordina secondo entrambe le lettere e numeri nelle stringhe

function sort(list) { 
    var i, l, mi, ml, x; 
    // copy the original array 
    list = list.slice(0); 

    // split the strings, converting numeric (integer) parts to integers 
    // and leaving letters as strings 
    for(i = 0, l = list.length; i < l; i++) { 
     list[i] = list[i].match(/(\d+|[a-z]+)/g); 
     for(mi = 0, ml = list[i].length; mi < ml ; mi++) { 
      x = parseInt(list[i][mi], 10); 
      list[i][mi] = !!x || x === 0 ? x : list[i][mi]; 
     } 
    } 

    // sort deeply, without comparing integers as strings 
    list = list.sort(function(a, b) { 
     var i = 0, l = a.length, res = 0; 
     while(res === 0 && i < l) { 
      if(a[i] !== b[i]) { 
       res = a[i] < b[i] ? -1 : 1; 
       break; 
      } 

      // If you want to ignore the letters, and only sort by numbers 
      // use this instead: 
      // 
      // if(typeof a[i] === "number" && a[i] !== b[i]) { 
      //  res = a[i] < b[i] ? -1 : 1; 
      //  break; 
      // } 

      i++; 
     } 
     return res; 
    }); 

    // glue it together again 
    for(i = 0, l = list.length; i < l; i++) { 
     list[i] = list[i].join(""); 
    } 
    return list; 
} 
+0

Pensavo che l'OP volesse ignorare le non cifre e ordinare le cifre. – jfriend00

+0

@ jfriend00: Hmmm ... potresti avere ragione. In tal caso, è possibile aggiungere una clausola 'typeof a [i] ===" numero "' nella funzione di confronto 'while while--loop – Flambino

0

Ordinamento avviene da sinistra a destra a meno che non si crea un algoritmo personalizzato. Le lettere o le cifre vengono confrontate prima con le cifre e poi con le lettere.

Tuttavia, ciò che si desidera ottenere come da esempio (a1, a9, a10) NON SI ACCADRÀ MAI. Ciò richiederebbe conoscere i dati in anticipo e dividere la stringa in ogni modo possibile prima di applicare l'ordinamento.

Un'alternativa finale sarebbe:

a) rompere ogni stringa da sinistra a destra ogni volta che è un cambiamento da lettera a cifre e viceversa; & b) quindi avviare l'ordinamento su quei gruppi da DESTRA a SINISTRA. Sarà un algoritmo molto impegnativo. Può essere fatto!

Infine, se si è il GENERATORE del "testo" originale, è necessario considerare NORMALIZZAZIONE dell'output in cui a1 a9 a10 potrebbe essere emesso come a01 a09 a10. In questo modo potresti avere il pieno cotnrol della versione finale dell'algoritmo.

Buona fortuna!

4

Usare questa funzione di confronto per l'ordinamento ..

function compareLists(a,b){ 
    var alist = a.split(/(\d+)/), // split text on change from anything to digit and digit to anything 
     blist = b.split(/(\d+)/); // split text on change from anything to digit and digit to anything 

    alist.slice(-1) == '' ? alist.pop() : null; // remove the last element if empty 
    blist.slice(-1) == '' ? blist.pop() : null; // remove the last element if empty 

    for (var i = 0, len = alist.length; i < len;i++){ 
     if (alist[i] != blist[i]){ // find the first non-equal part 
      if (alist[i].match(/\d/)) // if numeric 
      { 
       return +alist[i] - +blist[i]; // compare as number 
      } else { 
       return alist[i].localeCompare(blist[i]); // compare as string 
      } 
     } 
    } 

    return true; 
} 

Sintassi

var data = ["a1b3","a10b11","b10b2","a9b2","a1b20","a1c4"]; 
data.sort(compareLists); 
alert(data); 

demohttp://jsfiddle.net/h9Rqr/7/

0

Avevo bisogno di un modo per prendere una stringa mista e creare una stringa che potesse essere ordinata altrove, in modo che i numeri fossero ordinati numericamente e in ordine alfabetico. In base alle risposte sopra ho creato quanto segue, che riempie tutti i numeri in un modo che riesco a capire, ovunque appaiano nella stringa.

function padAllNumbers(strIn) { 
    // Used to create mixed strings that sort numerically as well as non-numerically 
    var patternDigits = /(\d+)/g; // This recognises digit/non-digit boundaries 
    var astrIn = strIn.split(patternDigits); // we create an array of alternating digit/non-digit groups 

    var result = ""; 

    for (var i=0;i<astrIn.length; i++) { 
     if (astrIn[i] != "") { // first and last elements can be "" and we don't want these padded out 
      if (isNaN(astrIn[i])) { 
       result += astrIn[i]; 
      } else { 
       result += padOneNumberString("000000000",astrIn[i]); 
      } 
     } 
    } 
    return result; 
} 

function padOneNumberString(pad,strNum,left) { 
    // Pad out a string at left (or right) 
    if (typeof strNum === "undefined") return pad; 
    if (typeof left === "undefined") left = true; 
    var padLen = pad.length - (""+ strNum).length; 
    var padding = pad.substr(0,padLen); 
    return left? padding + strNum : strNum + padding; 
} 
Problemi correlati