Сортировка многомерного массива не работает с десятичными числами - PullRequest
0 голосов
/ 08 декабря 2011

Этот вопрос связан со сценарием и основан на нем: Как отсортировать многомерный массив по нескольким столбцам в 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) Сортировка по нескольким столбцам работает нормально, но показывает ту же аномалию, поэтому я упростил ее до сортировки по одному столбцу для этого примера.

Я смотрел на это часами, но без удачи, и мог бы использовать свежий взгляд. Почему моя функция array.sort работает неправильно? Любые мысли будут оценены.

Ответы [ 2 ]

1 голос
/ 08 декабря 2011

Я не знаю о датах, но строки и числа можно оба отсортировать по:

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

< и > работают для обоих типов данных. Я изменил это в вашем коде, и он отлично работает: http://jsfiddle.net/Ft44x/6/ Я изменил функцию sortDesc. Любые другие ошибки, вероятно, вызваны тем, что ваш код слишком запутан, исправьте его!

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

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

1 голос
/ 08 декабря 2011

В вашей функции checkSortValues есть проблема, когда после того, как тип данных установлен на число, он также проверяет на положительную дату.

Кроме того, у вас есть ошибка в вашем IsNumeric тесте, где input.length вернет неопределенное. Вы должны закомментировать / изменить вторую часть теста.

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>

Вы можете подтвердить, что здесь , если вы хотите ...

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