This question is related to, and based off the script from, this question: How does one sort a multi dimensional array by multiple columns in JavaScript?. My solution worked at the time (or so I believe), but after making a series of changes to our custom tag to make it cross-browser compliant, I'm now having a new issue.
So I have the following piece of code. My apologies for the length; this was as far as I could simplify it while still replicating the issue:
<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>
If you open this in a browser, you will see an html table that spits out each of the array columns in the order they were entered (ie: random). If you click either of the buttons at the bottom of a column, it will sort the data and place it in the 3rd and 4th columns. My real application wipes and reloads the table, but this works for demonstration purposes.
Clicking the sort button at the bottom of the text column works fine; it sorts the array by names alphabetically. However, if you sort the number column you will see a change in order but by no means correct. The results are the same each time, but different if you click the number sort immediately or after a text search (since the search is based off the current state of the array and not the original array). This is exactly what I'm seeing in my application.
It took me a while to narrow it down, but here's what I've determined:
1) Text, date, and integers sort fine; only decimals do not.
2) Decimals sort according to the first digit, but ignore data after the decimal point.
3) Multi-column sort works fine, but shows this same anomaly, so I simplified it to a single-column sort for this example.
I've been staring at this for hours, but without luck, and could use a fresh set of eyes. Why is my array.sort function not working correctly? Any thoughts would be appreciated.