2011-05-23 20 views
7

He estado trabajando en este problema todo el día sin una buena solución. Google también ha sido de poca ayuda. Tengo un script que necesita aceptar una matriz bidimensional con un número desconocido de filas/columnas. La secuencia de comandos también debe aceptar una matriz unidimensional que contenga una lista de columnas para ordenar, y otra que contenga el orden para ordenar por. La llamada se verá un poco como esto:¿Cómo se puede ordenar una matriz multidimensional por varias columnas en JavaScript?

var orderList = {0,4,3,1}; 
var orderDir = {asc,desc,desc,asc}; 
dataArr = do2DArraySort(dataArr, orderList, orderDir); 

La función do2DArraySort debe devolver la matriz dataArr ordenados por la primera columna (en orden ascendente), luego por la quinta (en orden descendente), y luego por la tercera (en orden descendente), luego por el segundo (en orden descendente). Pude hacer dos niveles de profundidad utilizando el siguiente código, pero se vino abajo una vez que intenté agregar una tercera columna de clasificación. Entiendo por qué, pero no puedo encontrar una buena manera de hacerlo funcionar.

¿Hay una forma estándar de hacer esto? ¿Podría alguien señalarme un buen script en línea que pueda estudiar y usar como plantilla? ¿O alguien puede sugerir una modificación a mi código para que funcione?

Gracias!

//appends an array content to the original array 
function addToArray(originalArray, addArray) { 
    if (addArray.length != 0) { 
     var curLength = 0; 
     curLength = originalArray.length; 
     var maxLength = 0; 
     maxLength = curLength + addArray.length; 
     var itrerateArray = 0; 
     for (var r = curLength; r < maxLength; r++) { 
      originalArray[r] = addArray[itrerateArray]; 
      itrerateArray++; 
     } 
    } 
} 

function do2DArraySort(arrayToBeSorted, sortColumnArray, sortDirectionArray) { 
    if (arrayToBeSorted == "undefined" || arrayToBeSorted == "null") return arrayToBeSorted; 
    if (arrayToBeSorted.length == 0) return arrayToBeSorted; 
    if (sortColumnArray.length == 0) return arrayToBeSorted; 
    tempArray = arrayToBeSorted; 
    var totalLength = sortColumnArray.length; 
    for(var m = 0; m < totalLength; m++) { 
     if (m == 0) { 
      doBubbleSort(tempArray, tempArray.length, sortColumnArray[m], sortDirectionArray[m]);   
     } else {  
      doMultipleSort(tempArray, sortColumnArray[m], sortColumnArray[m-1], sortDirectionArray[m]); 
     } 
    } 
    return tempArray; 
} 

//check if a value exists in a single dimensional array 
function checkIfExists(arrayToSearch, valueToSearch) { 
    if (arrayToSearch == "undefined" || arrayToSearch == "null") return false; 
    if (arrayToSearch.length == 0) return false; 
    for (var k = 0; k < arrayToSearch.length; k++) { 
     if (arrayToSearch[k] == valueToSearch) return true; 
    } 
    return false; 
} 

//sorts an 2D array based on the distinct values of the previous column 
function doMultipleSort(sortedArray, currentCol, prevCol, sortDirection) { 
    var resultArray = new Array(); 
    var newdistinctValuesArray = new Array(); 
    //finding distinct previous column values 
    for (var n = 0; n < sortedArray.length; n++) { 
     if (checkIfExists(newdistinctValuesArray, sortedArray[n][prevCol]) == false) newdistinctValuesArray.push(sortedArray[n][prevCol]); 
    } 
    var recCursor = 0; 
    var newTempArray = new Array(); var toStoreArray = 0; 
    //for each of the distinct values 
    for (var x = 0; x < newdistinctValuesArray.length; x++) { 
     toStoreArray = 0; 
     newTempArray = new Array(); 
     //find the rows with the same previous column value 
     for (var y = 0; y < sortedArray.length; y++) { 
      if (sortedArray[y][prevCol] == newdistinctValuesArray[x]) { 
       newTempArray[toStoreArray] = sortedArray[y]; 
       toStoreArray++; 
      } 
     }  //sort the row based on the current column 
     doBubbleSort(newTempArray, newTempArray.length, currentCol, sortDirection); 
     //append it to the result array 
     addToArray(resultArray, newTempArray); 
    } 
    tempArray = resultArray; 
} 

Respuesta

23

Se prefiere la matriz literal [] sobre new Array. La notación {0,4,3,1} no es válida y debe ser [0,4,3,1].

¿Hay alguna necesidad de reinventar la rueda? Dos matrices se pueden unir usando:

originalArray = originalArray.concat(addArray); 

Los elementos pueden ser añadidos al final usando:

array.push(element); 

Arrays tienen un método para la clasificación de la matriz. Por defecto, está ordenada numéricamente:

// sort elements numerically 
var array = [1, 3, 2]; 
array.sort(); // array becomes [1, 2, 3] 

Las matrices también pueden invertirse. Continuando con el ejemplo anterior:

array = array.reverse(); //yields [3, 2, 1] 

Para proporcionar la clasificación personalizada, puede pasar el argumento de la función opcional de array.sort():

array = []; 
array[0] = [1, "first element"]; 
array[1] = [3, "second element"]; 
array[2] = [2, "third element"]; 
array.sort(function (element_a, element_b) { 
    return element_a[0] - element_b[0]; 
}); 
/** array becomes (in order): 
* [1, "first element"] 
* [2, "third element"] 
* [3, "second element"] 
*/ 

elementos conservarán su posición si el elemento es igual a otro elemento. Usando esto, puedes combinar múltiples algoritmos de clasificación. Debe aplicar sus preferencias de clasificación en orden inverso ya que el último tipo tiene prioridad sobre los anteriores.Para ordenar la matriz por debajo de la primera columna (orden descendente) y luego la segunda columna (orden ascendente):

array = []; 
array.push([1, 2, 4]); 
array.push([1, 3, 3]); 
array.push([2, 1, 3]); 
array.push([1, 2, 3]); 
// sort on second column 
array.sort(function (element_a, element_b) { 
    return element_a[1] - element_b[1]; 
}); 
// sort on first column, reverse sort 
array.sort(function (element_a, element_b) { 
    return element_b[0] - element_a[0]; 
}); 
/** result (note, 3rd column is not sorted, so the order of row 2+3 is preserved) 
* [2, 1, 3] 
* [1, 2, 4] (row 2) 
* [1, 2, 3] (row 3) 
* [1, 3, 3] 
*/ 

Para ordenar cadenas latino (es decir, en inglés, alemán, holandés), utilice String.localeCompare:

array.sort(function (element_a, element_b) { 
    return element_a.localeCompare(element_b); 
}); 

para ordenar la fecha de Date del objeto, utilice su representación milisegundos:

array.sort(function (element_a, element_b) { 
    return element_a.getTime() - element_b.getTime(); 
}); 

Se podría aplicar esta función de clasificación para todo tipo de datos, simplemente siga las reglas:

x es el resultado de la comparación de dos valores que deben ser devueltos por una función pasada a array.sort.

  1. x < 0: element_a debe venir antes de element_b
  2. x = 0: element_a y element_b son iguales, los elementos no se intercambian
  3. x > 0: element_a de venir después de element_b
+0

Gracias; esto es fantástico y muy esclarecedor Voy a investigar un poco más ahora. Parece que funciona de maravilla suponiendo que todos los elementos son numéricos, pero como mi matriz podría contener una combinación de números, fechas, caracteres, etc., tendré que encontrar la forma de modificar la función dentro del género para determinar el tipo de campo y ordenar en consecuencia . ¿Conoces un buen recurso al que podrías dirigirme para esto? – Nicholas

+0

@Nicholas: Incluí un ejemplo para ordenar las fechas del objeto 'Fecha' y las cadenas. Si comprende el uso de matrices y sabe un poco de Javascript, puede ordenar todo, siempre que tenga los requisitos para el algoritmo de clasificación. Si no está claro, agregue un comentario que pueda ser explicado. – Lekensteyn

+0

Lekensteyn; una vez más, gracias. Creo que entiendo bastante bien (ser peligroso) ahora;). Descubrí que el algoritmo básico de clasificación parece funcionar bien en las fechas sin tener que usar el método getTime. ¿Me estoy perdiendo algún error al omitir eso? Eres un verdadero salvavidas. :) – Nicholas

0

Yo sugeriría escribir una función de orden superior que toma la OrderList y orderDir como argumentos y devuelve una función de comparación que se pueden pasar directamente a la matriz # tipo. De esta forma puedes probar diferentes implementaciones (que intercambian simplicidad por el rendimiento, por ejemplo).

Este código no probado demuestra la idea:

var getComparator = function(orderList, orderDir) { 
    var len = orderList.length; // XXX: assume == orderDir.length 
    return function(a, b) { 
    var cmp, ax, bx, i; 
    for (i=0; i<len; i++) { # For each field and direction... 
     ax = a[orderList[i]]; 
     bx = b[orderList[i]]; 
     cmp = ax.localeCompare(bx); # compare elements... 
     if (cmp != 0) { # if not equal then indicate order... 
     return (orderDir[i]=='asc') ? -1 : 1; 
     } 
    } 
    return 0; # otherwise, indicate equality. 
    }; 
}; 
dataArr.sort(getComparator(orderList, orderDir)); 

Tenga en cuenta que usted querrá tener cuidado con el uso de "localeCompare" vs resta para cuerdas frente a los números, por lo que quizás ese aspecto se puede parametrizar a la función getComparator también.

0

Basado en excelente respuesta de Lekensteyn , He desarrollado la siguiente solución a mis necesidades. Todavía no he hecho pruebas de QA completas y no sé si es perfecto (de hecho, estoy bastante seguro de que no lo es), pero espero que otros puedan sacar provecho de esto y aprovecharlo para sus necesidades. Publicaré una actualización si se necesitan cambios importantes.

function do2DArraySort(dataArr, orderList, orderDir) { 
    for (x=orderList.length-1; x >= 0; x--) { 
     if (orderDir[x] == 'asc') { 
      dataArr.sort(sortMethodFunctionAsc); 
     } else { 
      dataArr.sort(sortMethodFunctionDesc); 
     } 
    } 

    return dataArr; 
} 

function sortMethodFunctionAsc(a, b) { 
    if ((IsNumeric(a[orderList[x]]) && IsNumeric(b[orderList[x]])) || (IsDate(a[orderList[x]]) && IsDate(b[orderList[x]]))) { 
     return a[orderList[x]] - b[orderList[x]]; 
    } else { 
     if (a[orderList[x]].toString() > b[orderList[x]].toString()) { 
      return 1; 
     } else if (a[orderList[x]].toString() < b[orderList[x]].toString()) { 
      return -1; 
     } else { 
      return 0; 
     } 
    } 
} 

function sortMethodFunctionDesc(a, b) { 
    if ((IsNumeric(a[orderList[x]]) && IsNumeric(b[orderList[x]])) || (IsDate(a[orderList[x]]) && IsDate(b[orderList[x]]))) { 
     return b[orderList[x]] - a[orderList[x]]; 
    } else { 
     if (a[orderList[x]].toString() < b[orderList[x]].toString()) { 
      return 1; 
     } else if (a[orderList[x]].toString() > b[orderList[x]].toString()) { 
      return -1; 
     } else { 
      return 0; 
     } 
    } 
} 


function IsNumeric(input) { 
    return (input - 0) == input && input.length > 0; 
} 

function IsDate(testValue) { 
    var returnValue = false; 
    var testDate; 
    try { 
     testDate = new Date(testValue); 
     if (!isNaN(testDate)) { 
      returnValue = true; 
     } else { 
      returnValue = false; 
     } 
    } 
    catch (e) { 
     returnValue = false; 
    } 
    return returnValue; 
} 
Cuestiones relacionadas