2010-05-06 12 views
72

Tengo una matriz multidimensional. La matriz primaria es una matriz de¿Cómo se ordena una matriz en varias columnas?

[publicationID][publication_name][ownderID][owner_name] 

Lo que estoy tratando de hacer es ordenar la matriz por owner_name y luego por publication_name. Sé que en JavaScript que tiene Array.sort(), en la que se puede poner una función personalizada, en mi caso tengo:

function mysortfunction(a, b) { 
    var x = a[3].toLowerCase(); 
    var y = b[3].toLowerCase(); 

    return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 
} 

Esto está bien para simplemente ordenar en una columna, es decir owner_name, pero ¿cómo puedo modificarlo ordenar en owner_name, luego publication_name?

Respuesta

113

Si los nombres de los propietarios son diferentes, ordene por ellos. De lo contrario, use el nombre de publicación para desempate.

function mysortfunction(a, b) { 

    var o1 = a[3].toLowerCase(); 
    var o2 = b[3].toLowerCase(); 

    var p1 = a[1].toLowerCase(); 
    var p2 = b[1].toLowerCase(); 

    if (o1 < o2) return -1; 
    if (o1 > o2) return 1; 
    if (p1 < p2) return -1; 
    if (p1 > p2) return 1; 
    return 0; 
} 
+0

@dcp No veo cómo podría ordenar el segundo atributo. A menos que lo bucle tantas como el número de columnas seleccionadas ... ¿Estoy en lo cierto? p.ej. '[[A, 10], [J, 15], [A, 5], [J, 5]] => [[A, 10], [A, 5], [J, 15], [J, 5]] ' –

+2

@ user26409021 - No, eso no está bien. Terminaría siendo [[A, 5], [A, 10], [J, 5], [J, 15]]. Primero ordena por el primer atributo, y si esos son iguales, entonces ordena por el segundo atributo. Entonces, en su ejemplo, A vendría antes que J. En el caso en que A sea igual para dos elementos, usaría el segundo atributo. Entonces, para [A, 10], [A, 5], 5 viene antes de 10, por lo que terminaría con [A, 5], [A, 10] para el pedido. Lo que puede faltar es que se llama a mysortfunction varias veces cuando usa Array.sort hasta que se completa la ordenación. – dcp

+1

@dcp, la parte que falta en su respuesta es el ciclo. Jaja. Gracias. En el mío repito su función tantas como las columnas seleccionadas –

16

Esto es útil para tipos alfa de todos los tamaños. Pásales los índices que quieras ordenar, en orden, como argumentos.

Array.prototype.deepSortAlpha= function(){ 
    var itm, L=arguments.length, order=arguments; 

    var alphaSort= function(a, b){ 
     a= a.toLowerCase(); 
     b= b.toLowerCase(); 
     if(a== b) return 0; 
     return a> b? 1:-1; 
    } 
    if(!L) return this.sort(alphaSort); 

    this.sort(function(a, b){ 
     var tem= 0, indx=0; 
     while(tem==0 && indx<L){ 
      itm=order[indx]; 
      tem= alphaSort(a[itm], b[itm]); 
      indx+=1;   
     } 
     return tem; 
    }); 
    return this; 
} 

var arr= [[ "Nilesh","Karmshil"], ["Pranjal","Deka"], ["Susants","Ghosh"], 
["Shiv","Shankar"], ["Javid","Ghosh"], ["Shaher","Banu"], ["Javid","Rashid"]]; 

arr.deepSortAlpha(1,0); 
+0

Puedo saber de dónde recolectaste estos datos [["Nilesh", "Karmshil"], ["Pranjal", "Deka"], ["Susants", "Ghosh"], ["Shiv", " Shankar "], [" Javid "," Ghosh "], [" Shaher "," Banu "], [" Javid "," Rashid "]]; – defau1t

19

Encontré una necesidad de hacer una matriz de objetos asc y desc mezclada estilo SQL ordena por claves. solución

de Kennebec arriba me ayudó a esto:

Array.prototype.keySort = function(keys) { 

keys = keys || {}; 

// via 
// https://stackoverflow.com/questions/5223/length-of-javascript-object-ie-associative-array 
var obLen = function(obj) { 
    var size = 0, key; 
    for (key in obj) { 
     if (obj.hasOwnProperty(key)) 
      size++; 
    } 
    return size; 
}; 

// avoiding using Object.keys because I guess did it have IE8 issues? 
// else var obIx = function(obj, ix){ return Object.keys(obj)[ix]; } or 
// whatever 
var obIx = function(obj, ix) { 
    var size = 0, key; 
    for (key in obj) { 
     if (obj.hasOwnProperty(key)) { 
      if (size == ix) 
       return key; 
      size++; 
     } 
    } 
    return false; 
}; 

var keySort = function(a, b, d) { 
    d = d !== null ? d : 1; 
    // a = a.toLowerCase(); // this breaks numbers 
    // b = b.toLowerCase(); 
    if (a == b) 
     return 0; 
    return a > b ? 1 * d : -1 * d; 
}; 

var KL = obLen(keys); 

if (!KL) 
    return this.sort(keySort); 

for (var k in keys) { 
    // asc unless desc or skip 
    keys[k] = 
      keys[k] == 'desc' || keys[k] == -1 ? -1 
      : (keys[k] == 'skip' || keys[k] === 0 ? 0 
      : 1); 
} 

this.sort(function(a, b) { 
    var sorted = 0, ix = 0; 

    while (sorted === 0 && ix < KL) { 
     var k = obIx(keys, ix); 
     if (k) { 
      var dir = keys[k]; 
      sorted = keySort(a[k], b[k], dir); 
      ix++; 
     } 
    } 
    return sorted; 
}); 
return this; 
}; 

ejemplos de uso:

var obja = [ 
    {USER:"bob", SCORE:2000, TIME:32, AGE:16, COUNTRY:"US"}, 
    {USER:"jane", SCORE:4000, TIME:35, AGE:16, COUNTRY:"DE"}, 
    {USER:"tim", SCORE:1000, TIME:30, AGE:17, COUNTRY:"UK"}, 
    {USER:"mary", SCORE:1500, TIME:31, AGE:19, COUNTRY:"PL"}, 
    {USER:"joe", SCORE:2500, TIME:33, AGE:18, COUNTRY:"US"}, 
    {USER:"sally", SCORE:2000, TIME:30, AGE:16, COUNTRY:"CA"}, 
    {USER:"yuri", SCORE:3000, TIME:34, AGE:19, COUNTRY:"RU"}, 
    {USER:"anita", SCORE:2500, TIME:32, AGE:17, COUNTRY:"LV"}, 
    {USER:"mark", SCORE:2000, TIME:30, AGE:18, COUNTRY:"DE"}, 
    {USER:"amy", SCORE:1500, TIME:29, AGE:19, COUNTRY:"UK"} 
]; 

var sorto = { 
    SCORE:"desc",TIME:"asc", AGE:"asc" 
}; 

obja.keySort(sorto); 

se obtiene la siguiente:

0: {  USER: jane;  SCORE: 4000; TIME: 35;  AGE: 16; COUNTRY: DE; } 
1: {  USER: yuri;  SCORE: 3000; TIME: 34;  AGE: 19; COUNTRY: RU; } 
2: {  USER: anita; SCORE: 2500; TIME: 32;  AGE: 17; COUNTRY: LV; } 
3: {  USER: joe;  SCORE: 2500; TIME: 33;  AGE: 18; COUNTRY: US; } 
4: {  USER: sally; SCORE: 2000; TIME: 30;  AGE: 16; COUNTRY: CA; } 
5: {  USER: mark;  SCORE: 2000; TIME: 30;  AGE: 18; COUNTRY: DE; } 
6: {  USER: bob;  SCORE: 2000; TIME: 32;  AGE: 16; COUNTRY: US; } 
7: {  USER: amy;  SCORE: 1500; TIME: 29;  AGE: 19; COUNTRY: UK; } 
8: {  USER: mary;  SCORE: 1500; TIME: 31;  AGE: 19; COUNTRY: PL; } 
9: {  USER: tim;  SCORE: 1000; TIME: 30;  AGE: 17; COUNTRY: UK; } 
keySort: { } 

(usando una función de impresión de here)

here is a jsbin example.

editar: cleaned up and posted as mksort.js on github.

42

creo que lo que estás buscando es thenBy.js: https://github.com/Teun/thenBy.js

Se le permite utilizar el estándar Array.sort, pero con firstBy().thenBy().thenBy() estilo.

An example can be seen here.

+0

Tenga cuidado con el rendimiento en grandes conjuntos de datos. Cada vez que se llama a 'thenBy', todos los elementos de la matriz se vuelven a pasar. –

+2

Eso definitivamente no es el caso. Cuando llamas a thenBy(), construye una nueva función que encapsula a la anterior.En el momento de la ordenación, javascript no "pasará" por los elementos, pero llamará a la función que la pasa muchas veces. El número de llamadas no cambiará al usar thenBy. Para algunas consideraciones de rendimiento, lea: https://github.com/Teun/thenBy.js#a-word-on-performance –

+1

Veo, estaba equivocado, gracias por pensar en el rendimiento. Tal vez, agregue una nota sobre las consideraciones de memoria para crear cierres con nuevas funciones. –

6

Puede combinar las 2 variables juntas en una clave de orden y usar eso para su comparación.

list.sort(function(a,b){ 
    var aCat = a.var1 + a.var2; 
    var bCat = b.var1 + b.var2; 
    return (aCat > bCat ? 1 : aCat < bCat ? -1 : 0); 
}); 
+0

@GustavoRodrigues tal vez porque es demasiado frágil. No podría ordenar de la manera esperada en ciertas teclas de entrada ya que solo une las dos partes juntas sin un delimitador u otra distinción. Considere si var1 y var2 para el elemento X eran "foo" y "baz", mientras que var1 para el elemento Y era "foobar". Cuando ordenamos X debería ser lo primero, pero en este caso sería el segundo. Esta respuesta podría mejorarse, pero como se dijo, no es seguro. –

0
function multiSort() { 

    var args =$.makeArray(arguments), 
     sortOrder=1, prop='', aa='', b=''; 

    return function (a, b) { 

     for (var i=0; i<args.length; i++){ 

     if(args[i][0]==='-'){ 
      prop=args[i].substr(1) 
      sortOrder=-1 
     } 
     else{sortOrder=1; prop=args[i]} 

     aa = a[prop].toLowerCase() 
     bb = b[prop].toLowerCase() 

     if (aa < bb) return -1 * sortOrder; 
     if (aa > bb) return 1 * sortOrder; 

     } 

     return 0 
    } 

} 
empArray.sort(multiSort('lastname','firstname')) Reverse with '-lastname' 
4

Es mejor utilizar construido en un comparador y el orden de clasificación de la cadena deseada con lógica o ||.

function customSort(a, b) { 
    return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]); 
} 

Ejemplo de trabajo:

var array = [ 
 
    [0, 'Aluminium', 0, 'Francis'], 
 
    [1, 'Argon', 1, 'Ada'], 
 
    [2, 'Brom', 2, 'John'], 
 
    [3, 'Cadmium', 3, 'Marie'], 
 
    [4, 'Fluor', 3, 'Marie'], 
 
    [5, 'Gold', 1, 'Ada'], 
 
    [6, 'Kupfer', 4, 'Ines'], 
 
    [7, 'Krypton', 4, 'Joe'], 
 
    [8, 'Sauerstoff', 3, 'Marie'], 
 
    [9, 'Zink', 5, 'Max'] 
 
]; 
 

 
array.sort(function (a, b) { 
 
    return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]); 
 
}); 
 

 
document.write('<pre>'); 
 
array.forEach(function (a) { 
 
    document.write(JSON.stringify(a) + '<br>'); 
 
});

1

estaba trabajando con ng-grid y necesitaba a múltiples columna de ordenación en una serie de registros devueltos desde una API, por lo que se le ocurrió esta ingeniosa y dinámica función de ordenamiento múltiple.

En primer lugar, ng-grid dispara un "evento" para "ngGridSorted" y pasa esta estructura de respaldo, que describe el tipo:

sortData = { 
    columns: DOM Element, 
    directions: [], //Array of string values desc or asc. Each index relating to the same index of fields 
    fields:  [], //Array of string values 
}; 

Así que construyeron una función que va a generar dinámicamente una función de clasificación basado en el sortData como se muestra arriba (no hay que asustarse por la barra de desplazamiento está a sólo 50 líneas de largo Además, me siento acerca de la decantación Se evitó una barra de desplazamiento horizontal!!.!):

function SortingFunction(sortData) 
{ 
    this.sortData = sortData; 

    this.sort = function(a, b) 
    { 
     var retval = 0; 

     if(this.sortData.fields.length) 
     { 
      var i = 0; 

      /* 
       Determine if there is a column that both entities (a and b) 
       have that are not exactly equal. The first one that we find 
       will be the column we sort on. If a valid column is not 
       located, then we will return 0 (equal). 
      */ 
      while( ( !a.hasOwnProperty(this.sortData.fields[i]) 
        || !b.hasOwnProperty(this.sortData.fields[i]) 
        || (a.hasOwnProperty(this.sortData.fields[i]) 
         && b.hasOwnProperty(this.sortData.fields[i]) 
         && a[this.sortData.fields[i]] === b[this.sortData.fields[i]]) 
        ) && i < this.sortData.fields.length){ 
       i++; 
      } 

      if(i < this.sortData.fields.length) 
      { 
       /* 
        A valid column was located for both entities 
        in the SortData. Now perform the sort. 
       */ 
       if(this.sortData.directions 
       && i < this.sortData.directions.length 
       && this.sortData.directions[i] === 'desc') 
       { 
        if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]]) 
         retval = -1; 
        else if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]]) 
         retval = 1; 
       } 
       else 
       { 
        if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]]) 
         retval = -1; 
        else if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]]) 
         retval = 1; 
       } 
      } 
     } 

     return retval; 
    }.bind(this); 
} 

entonces ordenar los resultados de mi API (results) de esta manera:

results.sort(new SortingFunction(sortData).sort); 

espero que alguien más disfruta de esta solución tanto como yo! ¡Gracias!

+0

¿Para qué sirve la opción de columnas en los datos de clasificación? –

7

Una buena forma de ordenar muchos campos que son cadenas es usar toLocaleCompare y el operador booleano ||.

Algo así como:

// Sorting record releases by name and then by title. 
releases.sort((oldRelease, newRelease) => { 
    const compareName = oldRelease.name.localeCompare(newRelease.name); 
    const compareTitle = oldRelease.title.localeCompare(newRelease.title); 

    return compareName || compareTitle; 
}) 

Si queremos ordenar en más campos, que podría simplemente cadena de la sentencia de retorno con los operadores booleanos más.

+0

como cuestión de hecho, podría ordenarlo con un '.reduce()' – ekkis

+0

, sin embargo, '.localCompare()' devuelve -1, 0, 1, así que no creo que su solución funcione como la | | es bueno para booleanos – ekkis

+3

@ekkis, tanto 1 como -1 son "verdad", así que esta es una solución muy elegante. Acabo de hacer esto: 'sortItems = (a, b) => (a.distance - b.distance) || (a.name - b.name); ' y funciona como un encanto para mis necesidades no exigentes. – bstst

Cuestiones relacionadas