2011-11-12 22 views
8

Me gustaría ordenar una matriz de cadenas (en javascript) de modo que los grupos de dígitos dentro de las cadenas se comparen como enteros y no como cadenas. No me preocupan los números con signo o coma flotante.cómo ordenar cadenas en javascript numéricamente

por ejemplo, el resultado debería ser ["a1b3","a9b2","a10b2","a10b11"] no ["a1b3","a10b11","a10b2","a9b2"]

La forma más sencilla de hacer esto parece ser la división de cada cadena de límites en torno a grupos de dígitos. ¿Hay algún patrón que pueda pasar a String.split para dividir los límites del personaje sin quitar ningún carácter?

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

O hay otra forma de comparar cadenas que no esté relacionado con dividirlos, tal vez mediante el relleno de todos los grupos de dígitos con ceros a la izquierda por lo que son de la misma longitud?

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

estoy trabajando con cadenas arbitrarias, no cadenas que tienen una disposición específica de grupos de dígitos.

Editar:

me gusta la /(\d+)/ un trazador de líneas de Gaby para dividir la matriz. ¿Qué tan compatible con versiones anteriores es eso?

Las soluciones que analizan las cadenas de una manera que se puede utilizar para reconstruir los originales son mucho más eficientes que esta función de comparación. Ninguna de las respuestas maneja algunas cadenas que comienzan con dígitos y otras no, pero eso sería bastante fácil de solucionar y no fue explícito en la pregunta original.

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

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

+0

¿Las partes no numéricas son siempre las mismas? De lo contrario, ¿debería el algoritmo de clasificación ordenarlos en orden ASCII? –

+1

En su ejemplo, están extrayendo 13, 92, 102, 1011? ¿O es más como 1.3, 9.2, 10.2, 10.11? Quiero decir, ¿es el primer número más significativo o las letras simplemente se ignoran? –

+0

... oh, todavía quieres ordenar los no enteros, lo entiendo ahora ... –

Respuesta

9

creo que esto hace lo que quiere

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

Funciona con stacksort. –

5

Suponiendo lo que quiere hacer es sólo hacer una ordenación numérica por los dígitos en cada entrada de la matriz (haciendo caso omiso de la no-dígitos), puede utilizar esto:

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

Utiliza una función de clasificación personalizada que elimina los dígitos y los convierte en un número cada vez que se le pide hacer una comparación. Puedes verlo funcionar aquí: http://jsfiddle.net/jfriend00/t87m2/.

Si esto no es lo que quieres, aclara que tu pregunta no es muy clara ya que el tipo debería funcionar.

+0

Creo que podría haber problemas si encuentra un número de cero, ¿no? es decir. abc03def45 –

+0

@ Dr.Dredel - El uso de parseInt lo convierte en un tipo numérico puro. Los ceros a la izquierda se ignorarán cuando se conviertan a un número verdadero como deberían ser. No veo ningún problema – jfriend00

+0

ah ... muy cierto ... nm –

1

Here's a more complete solution que ordena de acuerdo con letras y números en las cadenas

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

Pensé que el OP quería ignorar los no dígitos y simplemente ordenar por los dígitos. – jfriend00

+0

@ jfriend00: Hmmm ... puede que tengas razón. Si es así, puede agregar una cláusula 'typeof a [i] ===" number "' en la función de comparación 'while'-loop – Flambino

0

Clasificación ocurre de izquierda a derecha a menos que se crea un algoritmo personalizado. Las letras o dígitos son dígitos comparados primero y luego letras.

Sin embargo, lo que quiere lograr según su propio ejemplo (a1, a9, a10) NO SUCEDERÁ. Eso requeriría que conocieras los datos de antemano y dividir la cadena de todas las formas posibles antes de aplicar la clasificación.

Una última alternativa sería:

a) romper todos y cada cadena de izquierda a derecha cada vez que se produce un cambio entre letras y números, y viceversa; & b) luego comience la clasificación en esos grupos de DERECHO A IZQUIERDO. Ese será un algoritmo muy exigente. ¡Puede hacerse!

Finalmente, si usted es el GENERADOR del "texto" original, debe considerar NORMALIZAR la salida donde a1 a9 a10 podría ser superado como a01 a09 a10. De esta forma, podría tener un cronograma completo de la versión final del algoritmo.

¡Buena suerte!

4

Utilice esta función de comparación para la clasificación ..

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

Sintaxis

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

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

0

Necesitaba una forma de tomar una secuencia mixta y crear una cadena que pudiera ordenarse en otro lugar, de modo que los números se clasificaran numéricamente y las letras en orden alfabético. Basado en las respuestas anteriores, creé lo siguiente, que rellena todos los números de una manera que puedo entender, donde sea que aparezcan en la cadena.

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