对多维数组进行排序不能处理小数

Sorting a multi-dimensional array doesn't work with decimals

本文关键字:排序 不能 处理 小数 数组      更新时间:2023-09-26

这个问题与这个问题有关,并且基于这个问题的脚本:如何在JavaScript中按多列对多维数组进行排序?我的解决方案在当时是有效的(至少我相信是这样),但在对我们的自定义标签进行了一系列更改以使其跨浏览器兼容之后,我现在遇到了一个新问题。

所以我有下面的一段代码。很抱歉篇幅太长;这是我在复制问题的同时尽可能简化它:

<table border=1>
    <tr style="font-weight:bold;">
        <td>Begin Text</td>
        <td>Begin Number</td>
        <td>Sorted Text</td>
        <td>Sorted Number</td>
    </tr>
    <tr>
        <td id="r1c1"></td>
        <td id="r1c2"></td>
        <td id="r1c3"></td>
        <td id="r1c4"></td>
    </tr>
    <tr>
        <td id="r2c1"></td>
        <td id="r2c2"></td>
        <td id="r2c3"></td>
        <td id="r2c4"></td>
    </tr>
    <tr>
        <td id="r3c1"></td>
        <td id="r3c2"></td>
        <td id="r3c3"></td>
        <td id="r3c4"></td>
    </tr>
    <tr>
        <td id="r4c1"></td>
        <td id="r4c2"></td>
        <td id="r4c3"></td>
        <td id="r4c4"></td>
    </tr>
    <tr>
        <td id="r5c1"></td>
        <td id="r5c2"></td>
        <td id="r5c3"></td>
        <td id="r5c4"></td>
    </tr>
    <tr>
        <td id="r6c1"></td>
        <td id="r6c2"></td>
        <td id="r6c3"></td>
        <td id="r6c4"></td>
    </tr>
    <tr>
        <td id="r7c1"></td>
        <td id="r7c2"></td>
        <td id="r7c3"></td>
        <td id="r7c4"></td>
    </tr>
    <tr>
        <td id="r8c1"></td>
        <td id="r8c2"></td>
        <td id="r8c3"></td>
        <td id="r8c4"></td>
    </tr>
    <tr>
        <td><input type="button" onclick="testSort(1);" value="Sort"></td>
        <td><input type="button" onclick="testSort(2);" value="Sort"></td>
    </tr>
</table>
<script>
    function testSort(orderCol) {
        orderList = [orderCol];
        dataArr = do2DArraySort(dataArr, orderList, 'desc');
        for (x=1; x<=numRows; x++) {
            document.getElementById('r' + x + 'c3').innerHTML = dataArr[x-1][1];
            document.getElementById('r' + x + 'c4').innerHTML = dataArr[x-1][2];
        }
    }
    function TwoDimensionalArray(iRows, iCols) { 
        var i;
        var j;
        var a = new Array(iRows);
        for (i=0; i < iRows; i++) {
            a[i] = new Array(iCols);
            for (j=0; j < iCols; j++) {
                a[i][j] = "";
            }
        }
        return(a);
    }
    function do2DArraySort(dataArr, orderList, orderDir) {
        //Loop over each item in the list of sort columns.  For each one invoke the sort method on the array using the appropriate function.
        for (x=orderList.length-1; x >= 0; x--) {
            if (orderDir[x] == 'asc') {
                dataArr.sort(sortMethodFunctionAsc);
            } else {
                dataArr.sort(sortMethodFunctionDesc);
            }
        }
        return dataArr;
    }
    function checkSortValues(a, b) {
        var dataType = 'Text';
        if ((IsNumeric(a) && IsNumeric(b)) || (a == null && IsNumeric(b)) || (IsNumeric(a) && b == null)) {
            dataType = 'Numeric';
        }
        if ((IsDate(a) && IsDate(b)) || (a == null && IsDate(b)) || (IsDate(a) && b == null)) {
            dataType = 'Date';
        }
        return dataType;
    }
    function sortMethodFunctionAsc(a, b) {
        if (checkSortValues(a[orderList[x]], b[orderList[x]]) == 'Numeric') {
            //If the values are numeric, simply check which is larger than the other.
            return a[orderList[x]] - b[orderList[x]];
        } else if (checkSortValues(a[orderList[x]], b[orderList[x]]) == 'Date') {
            //If the values are dates they need converted to date objects.  95% of the time this is not necessary as they are already passed in as dates,
            //but the conversion is required to catch the few cases when they are not.
            var a2 = new Date(a[orderList[x]]);
            var b2 = new Date(b[orderList[x]]);
            //The getTime method is used to convert the dates into millisecond ticker equivalents for easier comparison.
            return a2.getTime() - b2.getTime();
        } else {
            //If one of the values is a string, convert both to a string and compare alphabetically.
            if (a[orderList[x]].toString() > b[orderList[x]].toString()) {
                return 1;
            } else if (a[orderList[x]].toString() < b[orderList[x]].toString()) {
                return -1;
            } else {
                //If they are the same, tell the sort to skip them.
                return 0;
            }
        }
    }
    function sortMethodFunctionDesc(a, b) {
        //This function is identical to the ascending one, but the comparison operators are reversed.
        if (checkSortValues(a[orderList[x]], b[orderList[x]]) == 'Numeric') {
            return b[orderList[x]] - a[orderList[x]];
        } else if (checkSortValues(a[orderList[x]], b[orderList[x]]) == 'Date') {
            var a2 = new Date(a[orderList[x]]);
            var b2 = new Date(b[orderList[x]]);
            return b2.getTime() - a2.getTime();
        } 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;
    }
    numRows = 8;
    orderList = [1];
    dataArr = TwoDimensionalArray(numRows, 2);
    dataArr[0][1] = 'Jimbo';
    dataArr[0][2] = 3;
    dataArr[1][1] = 'Jim';
    dataArr[1][2] = 0.65;
    dataArr[2][1] = 'Jackie';
    dataArr[2][2] = 1.25;
    dataArr[3][1] = 'John';
    dataArr[3][2] = 0.8;
    dataArr[4][1] = 'Jacob';
    dataArr[4][2] = 0.95;
    dataArr[5][1] = 'Jill';
    dataArr[5][2] = 0.85;
    dataArr[6][1] = 'Jan';
    dataArr[6][2] = 0.8;
    dataArr[7][1] = 'Jamie';
    dataArr[7][2] = 1.45;
    for (x=1; x<=numRows; x++) {
        document.getElementById('r' + x + 'c1').innerHTML = dataArr[x-1][1];
        document.getElementById('r' + x + 'c2').innerHTML = dataArr[x-1][2];
    }
</script>

如果你在浏览器中打开它,你会看到一个html表格,它按照输入的顺序(即:随机)吐出每个数组列。如果单击列底部的任何一个按钮,它将对数据进行排序,并将其放在第3列和第4列中。我的实际应用程序擦除并重新加载表,但这用于演示目的。

单击文本列底部的排序按钮可以正常工作;它按名称的字母顺序对数组进行排序。但是,如果您对数字列进行排序,您将看到顺序上的变化,但绝不是正确的。每次结果都是一样的,但是如果您立即或在文本搜索之后单击数字排序(因为搜索是基于数组的当前状态而不是原始数组),则结果会有所不同。这正是我在我的应用程序中看到的。

我花了一些时间来缩小范围,但这是我确定的:

1)文本、日期和整数排序良好;

2)小数按照第一个数字排序,忽略小数点之后的数据。

3)多列排序工作得很好,但显示出同样的异常,所以我将其简化为本例中的单列排序。

我已经盯着这个看了几个小时了,但是没有运气,我需要一个新的眼睛。为什么是我的数组。排序功能不能正常工作?

您的checkSortValues函数中存在问题,一旦数据类型设置为数字,则也会对日期进行测试。

同样,在IsNumeric测试中有一个错误,其中input.length将返回undefined。你应该注释掉/更改测试的第二部分。

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

如果您更改IsNumeric测试,如果您使用else if而不是if进行日期测试,我认为它解决了您的问题。

function checkSortValues(a, b) {
    var dataType = 'Text';
    if ((IsNumeric(a) && IsNumeric(b)) || (a == null && IsNumeric(b)) || (IsNumeric(a) && b == null)) {
        dataType = 'Numeric';
    }
    else if ((IsDate(a) && IsDate(b)) || (a == null && IsDate(b)) || (IsDate(a) && b == null)) {
        dataType = 'Date';
    }
    return dataType;
}

编辑:包括完整的代码与我建议的修改…

我已经测试过了,它对我有效,以正确的顺序排序。

<table border=1>
    <tr style="font-weight:bold;">
        <td>Begin Text</td>
        <td>Begin Number</td>
        <td>Sorted Text</td>
        <td>Sorted Number</td>
    </tr>
    <tr>
        <td id="r1c1"></td>
        <td id="r1c2"></td>
        <td id="r1c3"></td>
        <td id="r1c4"></td>
    </tr>
    <tr>
        <td id="r2c1"></td>
        <td id="r2c2"></td>
        <td id="r2c3"></td>
        <td id="r2c4"></td>
    </tr>
    <tr>
        <td id="r3c1"></td>
        <td id="r3c2"></td>
        <td id="r3c3"></td>
        <td id="r3c4"></td>
    </tr>
    <tr>
        <td id="r4c1"></td>
        <td id="r4c2"></td>
        <td id="r4c3"></td>
        <td id="r4c4"></td>
    </tr>
    <tr>
        <td id="r5c1"></td>
        <td id="r5c2"></td>
        <td id="r5c3"></td>
        <td id="r5c4"></td>
    </tr>
    <tr>
        <td id="r6c1"></td>
        <td id="r6c2"></td>
        <td id="r6c3"></td>
        <td id="r6c4"></td>
    </tr>
    <tr>
        <td id="r7c1"></td>
        <td id="r7c2"></td>
        <td id="r7c3"></td>
        <td id="r7c4"></td>
    </tr>
    <tr>
        <td id="r8c1"></td>
        <td id="r8c2"></td>
        <td id="r8c3"></td>
        <td id="r8c4"></td>
    </tr>
    <tr>
        <td><input type="button" onclick="testSort(1);" value="Sort"></td>
        <td><input type="button" onclick="testSort(2);" value="Sort"></td>
    </tr>
</table>
<script>
    function testSort(orderCol) {
        orderList = [orderCol];
        dataArr = do2DArraySort(dataArr, orderList, 'desc');
        for (x=1; x<=numRows; x++) {
            document.getElementById('r' + x + 'c3').innerHTML = dataArr[x-1][1];
            document.getElementById('r' + x + 'c4').innerHTML = dataArr[x-1][2];
        }
    }
    function TwoDimensionalArray(iRows, iCols) { 
        var i;
        var j;
        var a = new Array(iRows);
        for (i=0; i < iRows; i++) {
            a[i] = new Array(iCols);
            for (j=0; j < iCols; j++) {
                a[i][j] = "";
            }
        }
        return(a);
    }
    function do2DArraySort(dataArr, orderList, orderDir) {
        //Loop over each item in the list of sort columns.  For each one invoke the sort method on the array using the appropriate function.
        for (x=orderList.length-1; x >= 0; x--) {
            if (orderDir[x] == 'asc') {
                dataArr.sort(sortMethodFunctionAsc);
            } else {
                dataArr.sort(sortMethodFunctionDesc);
            }
        }
        return dataArr;
    }
    function checkSortValues(a, b) {
        var dataType = 'Text';
        if ((IsNumeric(a) && IsNumeric(b)) || (a == null && IsNumeric(b)) || (IsNumeric(a) && b == null)) {
            dataType = 'Numeric';
        }
        else if ((IsDate(a) && IsDate(b)) || (a == null && IsDate(b)) || (IsDate(a) && b == null)) {
            dataType = 'Date';
        }
        return dataType;
    }
    function sortMethodFunctionAsc(a, b) {
        if (checkSortValues(a[orderList[x]], b[orderList[x]]) == 'Numeric') {
            //If the values are numeric, simply check which is larger than the other.
            return a[orderList[x]] - b[orderList[x]];
        } else if (checkSortValues(a[orderList[x]], b[orderList[x]]) == 'Date') {
            //If the values are dates they need converted to date objects.  95% of the time this is not necessary as they are already passed in as dates,
            //but the conversion is required to catch the few cases when they are not.
            var a2 = new Date(a[orderList[x]]);
            var b2 = new Date(b[orderList[x]]);
            //The getTime method is used to convert the dates into millisecond ticker equivalents for easier comparison.
            return a2.getTime() - b2.getTime();
        } else {
            //If one of the values is a string, convert both to a string and compare alphabetically.
            if (a[orderList[x]].toString() > b[orderList[x]].toString()) {
                return 1;
            } else if (a[orderList[x]].toString() < b[orderList[x]].toString()) {
                return -1;
            } else {
                //If they are the same, tell the sort to skip them.
                return 0;
            }
        }
    }
    function sortMethodFunctionDesc(a, b) {
        //This function is identical to the ascending one, but the comparison operators are reversed.
        if (checkSortValues(a[orderList[x]], b[orderList[x]]) == 'Numeric') {
            return b[orderList[x]] - a[orderList[x]];
        } else if (checkSortValues(a[orderList[x]], b[orderList[x]]) == 'Date') {
            var a2 = new Date(a[orderList[x]]);
            var b2 = new Date(b[orderList[x]]);
            return b2.getTime() - a2.getTime();
        } 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;
    }
    numRows = 8;
    orderList = [1];
    dataArr = TwoDimensionalArray(numRows, 2);
    dataArr[0][1] = 'Jimbo';
    dataArr[0][2] = 3;
    dataArr[1][1] = 'Jim';
    dataArr[1][2] = 0.65;
    dataArr[2][1] = 'Jackie';
    dataArr[2][2] = 1.25;
    dataArr[3][1] = 'John';
    dataArr[3][2] = 0.8;
    dataArr[4][1] = 'Jacob';
    dataArr[4][2] = 0.95;
    dataArr[5][1] = 'Jill';
    dataArr[5][2] = 0.85;
    dataArr[6][1] = 'Jan';
    dataArr[6][2] = 0.8;
    dataArr[7][1] = 'Jamie';
    dataArr[7][2] = 1.45;
    for (x=1; x<=numRows; x++) {
        document.getElementById('r' + x + 'c1').innerHTML = dataArr[x-1][1];
        document.getElementById('r' + x + 'c2').innerHTML = dataArr[x-1][2];
    }
</script>

我不知道日期,但字符串和数字都可以排序:

if (a < b) {
    return 1;
} else if (b > a) {
    return -1;
} else {
    return
}

<>适用于两种数据类型。我在你的代码中改变了这一点,它工作得很好:http://jsfiddle.net/Ft44x/6/我已经改变了sortDesc函数。任何其他的bug都可能是由于你的代码太复杂造成的,清理它!

同样,反过来排序只是:不需要实现同样的事情两次。

function sortAsc() {
    ... // implement
}
function sortDesc(a, b) {
    return -1 * sortAsc(a,b);
}

另外,如果你有一个数字,不要把它存储为字符串,那就太愚蠢了。

var my_string = "foo";
var my_number = 42; // no quotes
var my_decimal = 0.63;

还有一件事,如果你需要知道某个东西是否是日期,为什么不使用instanceof呢?

var date = new Date();
alert(date instanceof Date);    // should be true

你可以对字符串和数字做同样的事情,但你甚至不需要:p