如何在JavaScript中按多列对多维数组进行排序?

时间:2011-05-23 18:45:14

标签: javascript arrays sorting multidimensional-array

我一整天都在努力解决这个问题而没有一个好的解决方案。谷歌一直没有什么帮助。我有一个脚本需要接受具有未知数量的行/列的二维数组。该脚本还需要接受一维数组,该数组包含要排序的列列表,另一个包含要排序的顺序。电话会看起来像这样:

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

5 个答案:

答案 0 :(得分:24)

数组文字[]优先于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 < 0element_a应该出现在element_b
  2. 之前
  3. x = 0element_aelement_b相等,元素未互换
  4. x > 0element_a应该在element_b
  5. 之后

答案 1 :(得分:0)

我建议编写一个高阶函数,它将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”vs减法,因此也许可以将该方面参数化为getComparator函数。

答案 2 :(得分:0)

基于Lekensteyn的出色反应,我根据自己的需求开发了以下解决方案。我还没有对它进行完整的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;
}

答案 3 :(得分:0)

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]

调用按列排序允许按单个列进行排序。 将第三行按数字排序。.

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]

然后将第二列按数字升序

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 然后将第一列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]

答案 4 :(得分:0)

这个问题已经有了很好的答案,想添加一个简短的函数来处理https://stackoverflow.com/users/2279116/shinobi的多键数组排序启发式解决方案。

// sort function handle for multiple keys
const sortCols  = (a, b, attrs) => Object.keys(attrs)
    .reduce((diff, k) =>  diff == 0 ? attrs[k](a[k], b[k]) : diff, 0);

让我们举一个例子

const array = [
    [1, 'hello', 4],
    [1, 'how', 3],
    [2, 'are', 3],
    [1, 'hello', 1],
    [1, 'hello', 3]
];

array.sort((a, b) => sortCols(a, b, { 
   0: (a, b) => a - b, 
   1: (a, b) => a.localeCompare(b), 
   2: (a, b) => b - a
}))

输出将紧随其后。

[ 1, "hello", 4 ]​
[ 1, "hello", 3 ]​
[ 1, "hello", 1 ]​
[ 1, "how", 3 ]
​[ 2, "are", 3 ]