2010-05-10 13 views
69

Estoy buscando la forma más fácil de ordenar una matriz que consta de números y texto, y una combinación de estos.Javascript: tipo natural de cadenas alfanuméricas

E.g.

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

convierte en

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

Esto va a ser utilizado en combinación con la solución a another question I've asked here.

La función de clasificación funciona, lo que necesito es una función que pueda decir que '19asd' es más pequeño que '123asd'.

Estoy escribiendo esto en JavaScript.

Editar: como adormitu señaló, lo que estoy buscando es una función para clasificar naturales

+0

ver también '¿Cómo hacerlo comparación de cadenas en JavaScript' en http://stackoverflow.com/questions/51165?/how-do-you-do-string-comparison-in-javascript –

+1

La pregunta original se formuló en 2010, por lo que no sería sorprendente :) – ptrn

Respuesta

102

Esto ahora es posible en los navegadores modernos usando localeCompare. Al pasar la opción numeric: true, reconocerá números inteligentemente. Puede hacer mayúsculas y minúsculas usando sensitivity: 'base'. Probado en Chrome, Firefox e IE11.

Aquí hay un ejemplo. Devuelve 1, lo que significa 10 va después de 2:

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

Para un rendimiento al clasificar un gran número de cuerdas, el artículo dice:

Al comparar un gran número de cadenas, como en la clasificación de grandes arrays, es mejor crear un objeto Intl.Collator y usar la función proporcionada por su propiedad de comparación.

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 para un one-liner moderno +1! – A1rPun

+1

Awesome, nice approach –

+1

Awesome, Has solucionado mi problema. Gracias –

38

lo que necesita una especie naturales?

Si es así, quizás this script by Brian Huisman based on David koelle's work sea lo que necesita.

Parece que la solución de Brian Huisman ahora se aloja directamente en el blog de David Koelle:

+0

Correcto, el tipo natural es lo que estoy buscando. Voy a mirar el enlace que enviaste, gracias – ptrn

+0

Esto ciertamente funcionó. ¡Muchas gracias! – ptrn

+0

Eso es un tipo muy antinatural. No produce un tipo alphbetic. – tchrist

17

para comparar los valores se puede utilizar una comparación de método-

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

Pero para la velocidad en sorti ng una matriz, rig the array antes de ordenar, , por lo que solo tiene que hacer conversiones en minúsculas y la expresión regular una vez en lugar de en cada paso del ordenamiento.

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

¿funcionaría esto en mi caso, con la matriz interna decidiendo el orden de la externa? – ptrn

+0

¿Qué es 'String.prototype.tlc()'? ¿Es este tu código o lo obtuviste de alguna parte? Si es el último, por favor enlace a la página. –

+0

disculpa por el error- corregido, gracias. Si desea que [1] yb [1] controlen la clasificación, use a = Cadena (a [1]). ToLowerCase(); b = Cadena (b [1]).toLowerCase(); – kennebec

0

Basándose en @Adrien Sé de respuesta anterior y utilizando el código que Brian Huisman & David koelle creado, aquí es un prototipo modificado de clasificación para una matriz de objetos:

//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

Imágenes de una función de relleno de 8 dígitos, una que transformaría '123asd' y '19asd' en '00000123asd' y '00000019asd' respectivamente. Usar esas versiones de cadenas nos ayudará a ordenar '19asd' para que aparezca antes de '123asd'.

Podemos utilizar la expresión regular /\d+/g para ayudar a encontrar todos los números que necesitan ser rellenados:

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

La siguiente muestra la clasificación utilizando esta técnica:

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

Los resultados intermedios muestran lo que hace la rutina natural_expand() y te da un comprensión de cómo funciona la rutina natural_compare posterior:

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

Salidas:

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