2011-11-12 13 views
8

Chciałbym posortować tablicę łańcuchów (w javascript) tak, że grupy cyfr w łańcuchach są porównywane jako liczby całkowite, a nie łańcuchy. Nie martwię się liczbami sygnowanymi lub zmiennoprzecinkowymi.jak sortować ciągi znaków w javascript numerycznie

na przykład, wynik powinien być ["a1b3","a9b2","a10b2","a10b11"] nie ["a1b3","a10b11","a10b2","a9b2"]

Najłatwiej to zrobić wydaje się być podział każdy ciąg na granicach wokół grup cyfr. Czy istnieje wzór, który mogę przekazać do String.split, aby podzielić granice postaci bez usuwania żadnych znaków?

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

Czy jest jakiś inny sposób porównywania ciągów znaków, które nie wiążą się dzieląc je, być może przez dopełnienie wszystkich grup cyfr z wiodącymi zerami tak są one tej samej długości?

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

Pracuję z dowolnych ciągów, a nie ciągi, które mają specyficzny układ grup cyfr.

Edit:

Lubię /(\d+)/ jeden wyłożenia z Gaby podzielić tablicę. Jak to jest zgodne z poprzednimi wersjami?

Rozwiązania, które analizują łańcuchy w sposób, który można wykorzystać do odbudowania oryginałów, są znacznie wydajniejsze niż ta funkcja porównywania. Żadna z odpowiedzi nie obsługuje niektórych ciągów zaczynających się cyframi, a innych nie, ale byłoby to łatwe do naprawienia i nie było jednoznaczne w pierwotnym pytaniu.

["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(); 

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

+0

Czy części nienumeryczne są zawsze takie same? Jeśli nie, to czy algorytm sortowania powinien je sortować w kolejności ASCII? –

+1

W twoim przykładzie wyodrębniasz 13, 92, 102, 1011? Czy może bardziej przypomina 1.3, 9.2, 10.2, 10.11? Chodzi mi o to, czy pierwszy numer jest bardziej znaczący, czy litery są po prostu ignorowane? –

+0

... o, nadal chcesz sortować na niecałkowatych też, teraz to rozumiem ... –

Odpowiedz

9

myślę, że robi to, co chcesz

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

Działa ze stacksort. –

5

Zakładając, co chcesz zrobić, to po prostu zrobić porządek numeryczny przez cyfr w każdym wpisie tablicy (ignorując non-cyfry), można użyć tego:

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

Używa funkcji sortowania niestandardowego, która usuwa cyfry i konwertuje na liczbę za każdym razem, gdy zostanie poproszony o porównanie. Możesz zobaczyć, że działa tutaj: http://jsfiddle.net/jfriend00/t87m2/.

Jeśli nie jest to, co chcesz, to proszę wyjaśnić, ponieważ pytanie nie jest bardzo jasne na temat tego, jak powinien wyglądać ten rodzaj.

+0

Myślę, że mogą wystąpić problemy, jeśli napotka zero zero, nie? to znaczy. abc03def45 –

+0

@ Dr.Dredel - Użycie parseInt powoduje, że jest to czysty numeryczny typ. Wiodące zera będą ignorowane po przekonwertowaniu na liczbę rzeczywistą, tak jak powinny. Nie widzę żadnego problemu. – jfriend00

+0

ah ... bardzo prawdziwe ... nm –

1

Here's a more complete solution że sortuje według liter i cyfr w ciągi

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

Myślałem, że OP chce zignorować cyfry i posortować je według cyfr. – jfriend00

+0

@ jfriend00: Hmmm ... możesz mieć rację. Jeśli tak, możesz dodać klauzulę 'typeof a [i] ===" number "' w funkcji porównania 'while'-loop – Flambino

0

Sortowanie występuje od lewej do prawej strony, chyba że stworzenie algorytmu niestandardowego. Litery lub cyfry są najpierw porównywane, a następnie litery.

Jednak to, co chcesz osiągnąć, jak na własny przykład (a1, a9, a10), NIGDY NIE ZOSTAŁO ZROBIONE. To wymagałoby poznania danych przed rozdaniem i podzielenia łańcucha w każdy możliwy sposób przed zastosowaniem sortowania.

Ostatnim rozwiązaniem byłoby:

a) zerwać każdy łańcuch od lewej do prawej, gdy następuje zmiana z listu do cyfry i odwrotnie; & b) następnie rozpocznij sortowanie w tych grupach od PRAWEJ do LEWEJ. To będzie bardzo wymagający algorytm. Można to zrobić!

Wreszcie, jeśli jesteś GENERATOREM oryginalnego "tekstu", powinieneś rozważyć NORMALIZOWANIE wyjścia, gdzie a1 a9 a10 może być wyprowadzone jako a01 a09 a10. W ten sposób możesz mieć pełne cotnrol ostatecznej wersji algorytmu.

Powodzenia!

4

użyć tej funkcji porównać do sortowania ..

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

Składnia

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

demo nahttp://jsfiddle.net/h9Rqr/7/

0

Potrzebowałem sposobu, aby wziąć mieszany ciąg i utworzyć ciąg, który można posortować gdzie indziej, aby numery były sortowane alfabetycznie i alfabetycznie. W oparciu o powyższe odpowiedzi stworzyłem następujące, które wyłapuje wszystkie liczby w sposób, który rozumiem, gdziekolwiek pojawiają się w łańcuchu.

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