Как отсортировать многомерный массив по нескольким столбцам в JavaScript? - PullRequest
9 голосов
/ 23 мая 2011

Я работал над этой проблемой весь день без хорошего решения.Гугл тоже мало чем помог.У меня есть скрипт, который должен принимать двумерный массив с неизвестным количеством строк / столбцов.Сценарий также должен принимать одномерный массив, содержащий список столбцов для сортировки, и другой, содержащий порядок сортировки.Вызов будет выглядеть примерно так:

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

Функция do2DArraySort должна возвращать массив dataArr, отсортированный по первому столбцу (в порядке возрастания), затем по пятому (в порядке убывания), затем потретий (в порядке убывания), затем на второй (в порядке убывания).Я смог сделать его на два уровня глубже, используя приведенный ниже код, но он развалился, когда я попытался добавить третий столбец сортировки.Я понимаю почему, но я не могу найти хороший способ заставить его работать.

Есть ли стандартный способ сделать это?Может ли кто-нибудь указать мне хороший онлайн-сценарий, который я могу изучать и использовать в качестве шаблона?Или кто-то может предложить модификацию моего кода, чтобы он работал?

Спасибо!

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

Ответы [ 4 ]

24 голосов
/ 23 мая 2011

Литерал массива [] предпочтительнее, чем new Array. Обозначение {0,4,3,1} недопустимо и должно быть [0,4,3,1].

Есть ли необходимость изобретать велосипед? Два массива можно объединить с помощью:

originalArray = originalArray.concat(addArray);

Элементы могут быть добавлены в конец, используя:

array.push(element);

Массивы имеют метод сортировки массива. По умолчанию он отсортирован по номерам:

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

Массивы также можно поменять местами. Продолжая предыдущий пример:

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

Чтобы обеспечить пользовательскую сортировку, вы можете передать необязательный аргумент функции в 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"]
 */

Элементы сохранят свою позицию, если элемент равен другому элементу. Используя это, вы можете комбинировать несколько алгоритмов сортировки. Вы должны применить свои настройки сортировки в обратном порядке, так как последняя сортировка имеет приоритет над предыдущими. Чтобы отсортировать массив ниже по первому столбцу (по убыванию), а затем по второму столбцу (по возрастанию):

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

Чтобы отсортировать латинские строки (например, английский, немецкий, голландский), используйте String.localeCompare:

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

Чтобы отсортировать даты из объекта Date, используйте их представление в миллисекундах:

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

Вы можете применить эту функцию сортировки ко всем видам данных, просто следуйте правилам:

x является результатом сравнения двух значений, которые должны быть возвращены функцией, переданной в array.sort.

  1. x < 0: element_a должно предшествовать element_b
  2. x = 0: element_a и element_b равны, элементы не поменялись местами
  3. x > 0: element_a должно следовать после element_b
0 голосов
/ 11 июля 2019
var arr = [27, 2, 4, 13]
arr.sort();

устанавливает arr как [13, 2, 27, 4], потому что массивы сортируются как строки по умолчанию в JavaScript

arr.sort(function (a, b) {
    return a - b;
});

устанавливает arr как [2, 4, 13, 27], сортируя численно вперед.

arr.sort(function (a, b) {
    return b - a;
});

устанавливает arr как [27, 13, 4, 2], сортируя численно в обратном порядке.

var marr = [[]];
marr.shift();

marr.push(["frog", 4, 27, 13]);
marr.push(["frog", 11, 5, 12]);
marr.push(["cat", 16, 3, 5]);
marr.push(["dog", 11, 7, 21]);
marr.push(["cat", 16, 21, 6]);
marr.push(["dog", 10, 280, 5]);
marr.push(["dog", 10, 32, 5]);

marr.sort();

устанавливает marr следующим образом, сортируя строки массива по столбцам по порядку в виде строк ..

["cat", 16, 21, 6]
["cat", 16, 3, 5]
["dog", 10, 280, 5]
["dog", 10, 32, 5]
["dog", 11, 7, 21]
["frog", 11, 5, 12]
["frog", 4, 27, 13]

вызов сортировки по столбцу позволяет сортировать по одному столбцу. сортировать строки по 3-му столбцу как число ..

marr.sort(function (a, b) {
    return a[2] - b[2];
});

["cat", 16, 3, 5]
["frog", 11, 5, 12]
["dog", 11, 7, 21]
["cat", 16, 21, 6]
["frog", 4, 27, 13]
["dog", 10, 32, 5]
["dog", 10, 280, 5]

затем сортировать 4-й столбец в обратном порядке как число ..

marr.sort(function (a, b) {
    return b[3] - a[3];
});

["dog", 11, 7, 21]
["frog", 4, 27, 13]
["frog", 11, 5, 12]
["cat", 16, 21, 6]
["cat", 16, 3, 5]
["dog", 10, 32, 5]
["dog", 10, 280, 5]

затем сортируйте 2-й столбец по возрастанию как число

marr.sort(function (a, b) {
    return a[1] - b[1];
});

["frog", 4, 27, 13]
["dog", 10, 32, 5]
["dog", 10, 280, 5]
["dog", 11, 7, 21]
["frog", 11, 5, 12]
["cat", 16, 21, 6]
["cat", 16, 3, 5]

уведомление каждый раз, когда вы сортируете, сохраняется предыдущий порядок, где новый столбец совпадает с последовательной строкой.

Теперь вы можете выполнить альфа-сортировку первого столбца

// asc
marr.sort(function (a, b) {
    return (a[0] < b[0]) ? -1 : 1;
});

["cat", 16, 21, 6]
["cat", 16, 3, 5]
["dog", 10, 32, 5]
["dog", 10, 280, 5]
["dog", 11, 7, 21]
["frog", 4, 27, 13]
["frog", 11, 5, 12]

// desc
marr.sort(function (a, b) {
    return (a[0] > b[0]) ? -1 : 1;
});

["frog", 4, 27, 13]
["frog", 11, 5, 12]
["dog", 10, 32, 5]
["dog", 10, 280, 5]
["dog", 11, 7, 21]
["cat", 16, 21, 6]
["cat", 16, 3, 5]

отсортировать все числовые столбцы в цикле desc: 4, 3, 2 затем отсортируйте 1-й столбец asc как строку

for (var colid = 3; colid > 0; colid--) {
    marr.sort(function (a, b) {
        return (b[colid] - a[colid]);
    });
}

// 1st row as string asc
marr.sort(function (a, b) {
    return (a[0] < b[0]) ? -1 : 1;
});

["cat", 16, 21, 6]
["cat", 16, 3, 5]
["dog", 11, 7, 21]
["dog", 10, 280, 5]
["dog", 10, 32, 5]
["frog", 11, 5, 12]
["frog", 4, 27, 13]

объединить эти сортировки .. более логичным образом, в порядке того, какой столбец вы хотите отсортировать и как вы хотите, чтобы он сортировался первым

// c1 asc, c2 desc, c3 asc, c4 asc
marr.sort(function (a, b) {
    return (a[0] < b[0]) ? -1 : (a[0] == b[0]) ?
        (b[1] - a[1]) || (a[2] - b[2]) || (a[3] - b[3]) : 1;
});

["cat", 16, 3, 5]
["cat", 16, 21, 6]
["dog", 11, 7, 21]
["dog", 10, 32, 5]
["dog", 10, 280, 5]
["frog", 11, 5, 12]
["frog", 4, 27, 13]
0 голосов
/ 24 мая 2011

Основываясь на превосходном ответе Лекенштейна, я разработал следующее решение для своих нужд. Я еще не провел полное тестирование QA и не знаю, является ли он идеальным (на самом деле, я совершенно уверен, что это не так), но я надеюсь, что другие могут извлечь из этого пользу и использовать его для их потребности. Я опубликую обновление, если будут внесены какие-либо важные изменения.

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;
}
0 голосов
/ 23 мая 2011

Я бы предложил написать функцию высшего порядка, которая принимает в качестве аргументов orderList и orderDir и возвращает функцию компаратора, которую можно напрямую передать в Array # sort.Таким образом, вы можете попробовать разные реализации (например, которые компенсируют простоту для производительности).

Этот непроверенный код демонстрирует идею:

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

Обратите внимание, что вам нужно быть осторожнымоб использовании "localeCompare" против вычитания для строк против чисел, так что, возможно, этот аспект может быть параметризован и для функции getComparator.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...