deal with strings in numeric sorts

This commit is contained in:
Rob Garrison 2011-07-31 22:15:17 -05:00
parent acb8e73515
commit 7ca39974e7
9 changed files with 324 additions and 67 deletions

View File

@ -26,7 +26,17 @@ Included all original [document pages](http://mottie.github.com/tablesorter/docs
###Change Log
View the [complete listing here](http://mottie.github.com/tablesorter/changelog.markdown).
View the [complete listing here](http://mottie.github.com/tablesorter/changelog.txt).
#### Version 2.0.10 (2011-07-31)
* Modified the numeric sort with a new method to deal with non-numeric content:
* When sorting columns with numeric values, by default any non-numeric or empty cells are treated as if they have a zero value. This puts the text between negative and positive values in a column.
* Adding `string : "max+"` to the `headers` option will now treat any non-numeric table cells as if they have a maxiumum positive value (a value greater than the maximum positive value in the column).
* Adding `string : "max-"` to the `headers` option will now treat any non-numeric table cells as if they have a maxiumum negative value (a value greater than the maximum negative value in the column).
* See the "[Dealing with text strings in numeric sorts](http://mottie.github.com/tablesorter/docs/example-options-headers-digits-strings.html)" demo for a better visual example.
* Changed UI theme widget code to use "ui-widget-header" instead of "ui-widget-default" to better match the themes.
* Renamed changelog.markdown to changelog.txt to prevent downloading when clicking on the link
#### Version 2.0.9 (2011-07-27)
@ -50,20 +60,3 @@ View the [complete listing here](http://mottie.github.com/tablesorter/changelog.
* Additionally, if the number of table rows is less than the pager size, the pager will get the `cssDisabled` class name applied.
* If false (the default setting), the pager arrows class names will not change.
* Please see the updated [pager demo](http://mottie.github.com/tablesorter/docs/example-pager.html) to see this working.
#### Version 2.0.8 (2011-07-21)
* Fixed parsers for currency and digits to work with number values separated by commas. Thanks to Josh Renaud for the information!
* Fixed "lockedOrder" header option and added documentation and an example on how to use it.
* Made the sort order "desc" only trigger off of the first letter, so any word/abbreviation starting with "d" will set the descending sort order, all other letters will set the order to ascending (shhh, because I'm a bad speller :P)
* Modified the "sortInitialOrder" option so it can also be set in the headers option.
#### Version 2.0.7 (2011-07-17)
* Added "pagerChange" and "pagerComplete" events to the pager plugin which trigger on the table. See the [pager demo](http://mottie.github.com/tablesorter/docs/example-pager.html) for an example on how to bind to them.
* Added the "sortAppend" since the option was there, but apparently the code wasn't.
* Added missing documentation from [my blog post](http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html)
* This included a few new example pages: apply widgets, child rows, render header, sort append and zebra widget.
* Added a methods and events table.
* Fixed the minified version. Apparently sorting functions called by the eval were removed by the Google Closure Compiler. Resolved by using "Whitespace only" optimization.
* Fixed syntax highlighting; updated Chili.

View File

@ -1,5 +1,15 @@
###TableSorter Change Log
#### Version 2.0.10 (2011-07-31)
* Modified the numeric sort with a new method to deal with non-numeric content:
* When sorting columns with numeric values, by default any non-numeric or empty cells are treated as if they have a zero value. This puts the text between negative and positive values in a column.
* Adding `string : "max+"` to the `headers` option will now treat any non-numeric table cells as if they have a maxiumum positive value (a value greater than the maximum positive value in the column).
* Adding `string : "max-"` to the `headers` option will now treat any non-numeric table cells as if they have a maxiumum negative value (a value greater than the maximum negative value in the column).
* See the "[Dealing with text strings in numeric sorts](http://mottie.github.com/tablesorter/docs/example-options-headers-digits-strings.html)" demo for a better visual example.
* Changed UI theme widget code to use "ui-widget-header" instead of "ui-widget-default" to better match the themes.
* Renamed changelog.markdown to changelog.txt to prevent downloading when clicking on the link
#### Version 2.0.9 (2011-07-27)
* Added a jQuery UI theme and widget example. To apply the jQuery UI theme:

View File

@ -9,6 +9,9 @@ h2{color:#333;font-size:small;font-weight:400;margin:0;}
table.info{border:#000 1px solid;border-collapse:collapse;margin:10px 0 0 10px;}
table.tablesorter table.info tbody th,table.tablesorter table.info tbody td{border:#000 1px solid;}
table.tablesorter table.info tbody th{background:#eee;}
#options{width:100%;}
#options pre{width:95%;}
#options .examples{width:60px;}
pre,#display{overflow-x:auto;padding:15px;border:1px solid #ddd;border-left-width:5px;}
pre,#display,code.hilight{background-color:#eee;color:#333;font-size:small;list-style:none;}
a code.hilight {text-decoration:underline;}

View File

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<title>jQuery plugin: Tablesorter 2.0 - Enabling debug mode</title>
<title>jQuery plugin: Tablesorter 2.0 - Dealing with Digits</title>
<!-- jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"></script>
@ -108,7 +108,7 @@
<div class="next-up">
<hr />
Next up: <a href="example-option-sort-order.html">Basic: Direction of initial sort &rsaquo;&rsaquo;</a>
Next up: <a href="example-options-headers-digits-strings.html">Basic: Dealing with text strings in numerical sorts &rsaquo;&rsaquo;</a>
</div>
</div>

View File

@ -0,0 +1,213 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery plugin: Tablesorter 2.0 - Dealing with text strings in numerical sorts</title>
<!-- jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"></script>
<!-- Demo stuff -->
<link rel="stylesheet" href="css/jq.css">
<script src="js/chili/jquery.chili-2.2.js"></script>
<script src="js/chili/recipes.js"></script>
<script src="js/docs.js"></script>
<!-- Tablesorter: required -->
<link rel="stylesheet" href="../css/blue/style.css">
<style>
th { width: 10%; }
</style>
<script src="../js/jquery.tablesorter.js"></script>
<script id="js">$(function() {
// call the tablesorter plugin
$("table").tablesorter({
headers: {
5: { sorter: "digit", string: "max+" }, // strings (non-numeric) cells are treated as a MAX value
6: { sorter: "digit", string: "max-" }, // strings (non-numeric) cells are treated as a MIN value
7: { sorter: "digit", string: "max+" }, // strings (non-numeric) cells are treated as a MAX value
8: { sorter: "digit", string: "max-" }, // strings (non-numeric) cells are treated as a MIN value
9: { sorter: "digit" } // strings (non-numeric) cells are given a value of ZERO (default)
}
});
});</script>
</head>
<body>
<div id="banner">
<h1>table<em>sorter</em></h1>
<h2>Dealing with text strings in numerical sorts</h2>
<h3>Flexible client-side table sorting</h3>
<a href="index.html">Back to documentation</a>
</div>
<div id="main">
<p class="tip">
<em>NOTE!</em>
<ul>
<li>When a column is sorted numerically ( sorter:"digit" ) any text also in that column will by default be given a zero value. Sort the last column to see the problem with this method.</li>
<li>Set the "string" to "max+" to treat any text as a value greater than the max numerical value in the column</li>
<li>Set the "string" to "max-" to treat any text as a value greater than the max negative value.</li>
<li>Sort columns five through eight to see how the sort is changed. Note that the text is sorted separately.</li>
</ul>
</p>
<h1>Demo</h1>
<div id="demo"><table>
<thead>
<tr>
<th>Account #</th>
<th>First Name</th>
<th>Last Name</th>
<th>Age</th>
<th>Difference</th>
<th>5: Ratings (max+)</th>
<th>6: Ratings (max-)</th>
<th>7: Change (max+)</th>
<th>8: Change (max-)</th>
<th>9: Change (none)</th>
</tr>
</thead>
<tbody>
<tr>
<td>A43</td>
<td>Peter</td>
<td>Parker</td>
<td>28</td>
<td>-35</td>
<td>01</td>
<td>01</td>
<td>-.1</td>
<td>-.1</td>
<td>-.1</td>
</tr>
<tr>
<td>A255</td>
<td>John</td>
<td>Hood</td>
<td>33</td>
<td>33</td>
<td>02</td>
<td>02</td>
<td>N/A #1</td>
<td>N/A #1</td>
<td>N/A #1</td>
</tr>
<tr>
<td>A33</td>
<td>Clark</td>
<td>Kent</td>
<td>18</td>
<td>2</td>
<td>03</td>
<td>03</td>
<td>N/A #2</td>
<td>N/A #2</td>
<td>N/A #2</td>
</tr>
<tr>
<td>A1</td>
<td>Bruce</td>
<td>Almighty</td>
<td>45</td>
<td>-5</td>
<td>04</td>
<td>04</td>
<td>-8.4</td>
<td>-8.4</td>
<td>-8.4</td>
</tr>
<tr>
<td>A102</td>
<td>Bruce</td>
<td>Evans</td>
<td>22</td>
<td>99</td>
<td>05</td>
<td>05</td>
<td>-2.2</td>
<td>-2.2</td>
<td>-2.2</td>
</tr>
<tr>
<td>A10</td>
<td>James</td>
<td>Sullivan</td>
<td>46</td>
<td>-1</td>
<td>06</td>
<td>06</td>
<td>97.4</td>
<td>97.4</td>
<td>97.4</td>
</tr>
<tr>
<td>A02</td>
<td>Bruce</td>
<td>Campbell</td>
<td>32</td>
<td>0</td>
<td>07</td>
<td>07</td>
<td>23.6</td>
<td>23.6</td>
<td>23.6</td>
</tr>
<tr>
<td>A55</td>
<td>Frank</td>
<td>Evans</td>
<td>51</td>
<td>44</td>
<td></td>
<td></td>
<td>11.4</td>
<td>11.4</td>
<td>11.4</td>
</tr>
<tr>
<td>A87</td>
<td>Joe</td>
<td>Smith</td>
<td>24</td>
<td>04</td>
<td>NR</td>
<td>NR</td>
<td>5.2</td>
<td>5.2</td>
<td>5.2</td>
</tr>
<tr>
<td>A012</td>
<td>Mike</td>
<td>Wazowski</td>
<td>21</td>
<td>-24</td>
<td>NR</td>
<td>NR</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table></div>
<h1>Javascript</h1>
<div id="javascript">
<pre class="js"></pre>
</div>
<h1>HTML</h1>
<div id="html">
<pre class="html"></pre>
</div>
<div class="next-up">
<hr />
Next up: <a href="example-option-sort-order.html">Basic: Direction of initial sort &rsaquo;&rsaquo;</a>
</div>
</div>
</body>
</html>

View File

@ -38,8 +38,8 @@
$(table).addClass('ui-widget ui-widget-content ui-corner-all');
$.each(c.headerList, function(){
$(this)
// using new "ui-theme" class in case the user adds their own ui-icon using onRenderHeader
.addClass('ui-state-default ui-corner-all ui-theme')
// using "ui-theme" class in case the user adds their own ui-icon using onRenderHeader
.addClass('ui-widget-header ui-corner-all ui-theme')
.append('<span class="ui-theme"/>');
});
}

View File

@ -36,7 +36,7 @@
</div>
<p>
<strong>Author:</strong> <a class="external" href="http://lovepeacenukes.com">Christian Bach</a><br>
<strong>Version:</strong> 2.0.9 (<a href="../changelog.markdown">changelog</a>)<br>
<strong>Version:</strong> 2.0.10 (<a href="../changelog.markdown">changelog</a>)<br>
<strong>Licence:</strong>
Dual licensed under <a class="external" href="http://www.opensource.org/licenses/mit-license.php">MIT</a>
or <a class="external" href="http://www.opensource.org/licenses/gpl-license.php">GPL</a> licenses.
@ -295,6 +295,7 @@
<li><a href="example-option-sort-force.html">Force a default sorting order</a></li>
<li><a href="example-option-sort-append.html">Append a sort to the selected sorting order</a></li>
<li><a href="example-option-digits.html">Dealing with digits!</a></li>
<li><a href="example-options-headers-digits-strings.html">Dealing with text strings in numerical sorts</a> <span class="tip"><em>New! v2.0.10</em></span></li>
<li><a href="example-option-sort-order.html">Direction of initial sort</a></li>
<li><a href="example-apply-widget.html">Applying widgets</a></li>
<li><a href="example-zebra.html">Applying the zebra stripe widget</a></li>
@ -344,7 +345,7 @@
<th>Type</th>
<th>Default</th>
<th>Description</th>
<th>Link</th>
<th class="examples">Link</th>
</tr>
</thead>
<tbody>
@ -508,19 +509,33 @@
<pre class="js">$(function(){
$("table").tablesorter({
headers: {
0: { sorter: false }, // disable first column - see Example 1
1: { sorter: "digit" }, // sort second column numerically
2: { sorter: "shortDate" } // sort the fifth column by date (e.g. mm/dd/yyyy if the date format is "us")
3: { lockedOrder: "asc" }, // lock the sort order - this option will not work if added as metadata - see Example 2
4: { sortInitialOrder: "desc" }, // Initial sort order direction of fourth column - see Example 3
// See example - Disable first column
0: { sorter: false },
// See example 2: Sort column numerically &amp; treat any text as if its value is:
1: { sorter: "digit" }, // zero
2: { sorter: "digit", string: "max+" }, // maximum positive value
3: { sorter: "digit", string: "max-" }, // maximum negative value
// Sort the fifth column by date (e.g. mm/dd/yyyy if the date format is "us")
4: { sorter: "shortDate" }
// See example 3: lock the sort order - this option will not work if added as metadata
5: { lockedOrder: "asc" },
// See Example 4: Initial sort order direction of seventh column
6: { sortInitialOrder: "desc" }
}
});
});</pre></div>
</td>
<td>
Ex:<a href="example-options-headers.html">1</a>
<a href="example-options-headers-locked.html">2</a>
<a href="example-options-headers-order.html">3</a>
<a href="example-options-headers-digits-strings.html">2</a>
<a href="example-options-headers-locked.html">3</a>
<a href="example-options-headers-order.html">4</a>
</td>
</tr>

View File

@ -1,6 +1,6 @@
/*
* TableSorter 2.0 - Client-side table sorting with ease!
* Version 2.0.9
* Version 2.0.10
* @requires jQuery v1.2.3
*
* Copyright (c) 2007 Christian Bach
@ -20,7 +20,7 @@
* @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
* @desc Create a tablesorter interface and disableing the first and second column headers.
*
* @example $('table').tablesorter({ headers: { 0: {sorter:"integer"}, 1: {sorter:"currency"} } });
* @example $('table').tablesorter({ headers: { 0: {sorter:"digit"}, 1: {sorter:"currency"} } });
* @desc Create a tablesorter interface and set a column parser for the first and second column.
*
* @param Object settings An object literal containing key/value pairs to provide optional settings.
@ -483,14 +483,23 @@
/* sorting methods - reverted sorting method back to version 2.0.3 */
function multisort(table,sortList,cache) {
var dynamicExp = "var sortWrapper = function(a,b) {",
l = sortList.length, sortTime, i, c, s, e, order, orgOrderCol;
if (table.config.debug) { sortTime = new Date(); }
col, mx = 0, dir = 0, tc = table.config, lc = cache.normalized.length,
l = sortList.length, sortTime, i, j, c, s, e, order, orgOrderCol;
if (tc.debug) { sortTime = new Date(); }
for (i=0; i < l; i++) {
c = sortList[i][0];
order = sortList[i][1];
s = (getCachedSortType(table.config.parsers,c) === "text") ? ((order === 0) ? "sortText" : "sortTextDesc") : ((order === 0) ? "sortNumeric" : "sortNumericDesc");
s = (getCachedSortType(tc.parsers,c) === "text") ? ((order === 0) ? "sortText" : "sortTextDesc") : ((order === 0) ? "sortNumeric" : "sortNumericDesc");
e = "e" + i;
dynamicExp += "var " + e + " = " + s + "(a[" + c + "],b[" + c + "]); ";
// get max column value (ignore sign)
if (/Numeric/.test(s) && tc.headers[c] && tc.headers[c].string){
for (j=0; j < lc; j++) {
col = Math.abs(parseFloat(cache.normalized[j][c]));
mx = Math.max( mx, isNaN(col) ? 0 : col );
}
dir = (tc.headers[c]) ? tc.string[tc.headers[c].string] || 0 : 0;
}
dynamicExp += "var " + e + " = " + s + "(a[" + c + "],b[" + c + "]," + mx + "," + dir + "); ";
dynamicExp += "if (" + e + ") { return " + e + "; } ";
dynamicExp += "else { ";
}
@ -504,11 +513,11 @@
dynamicExp += "}; ";
eval(dynamicExp);
cache.normalized.sort(sortWrapper);
if (table.config.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order+ " time:", sortTime); }
if (tc.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order+ " time:", sortTime); }
return cache;
}
// http://www.webdeveloper.com/forum/showthread.php?t=107909
// Natural sort modified from: http://www.webdeveloper.com/forum/showthread.php?t=107909
function sortText(a, b) {
if ($.data(tbl[0], "tablesorter").sortLocaleCompare) { return a.localeCompare(b); }
if (a === b) { return 0; }
@ -537,16 +546,36 @@
}
}
function sortTextDesc(a,b){
function sortTextDesc(a, b){
if ($.data(tbl[0], "tablesorter").sortLocaleCompare) { return b.localeCompare(a); }
return -sortText(a,b);
return -sortText(a, b);
}
function sortNumeric(a, b) {
// return text string value by adding up ascii value
// so the text is somewhat sorted when using a digital sort
// this is NOT an alphanumeric sort
function getTextValue(a, mx, d){
if (a === '') { return (d || 0) * Number.MAX_VALUE; }
if (mx) {
// make sure the text value is greater than the max numerical value (mx)
var i, l = a.length, n = mx + d;
for (i = 0; i < l; i++){
n += a.charCodeAt(i);
}
return d * n;
}
return 0;
}
function sortNumeric(a, b, mx, d) {
if (a === '' || isNaN(a)) { a = getTextValue(a, mx, d); }
if (b === '' || isNaN(b)) { b = getTextValue(b, mx, d); }
return a - b;
}
function sortNumericDesc(a, b) {
function sortNumericDesc(a, b, mx, d) {
if (a === '' || isNaN(a)) { a = getTextValue(a, mx, d); }
if (b === '' || isNaN(b)) { b = getTextValue(b, mx, d); }
return b - a;
}
@ -570,6 +599,8 @@
$headers = buildHeaders(this);
// try to auto detect column type, and store in tables config
this.config.parsers = buildParserCache(this, $headers);
// digit sort text location
this.config.string = { max: 1, 'max+': 1, 'max-': -1, none: 0 };
// build the cache for the tbody cells
cache = buildCache(this);
// get the css class names, could be done else where.
@ -662,8 +693,7 @@
var me = this;
setTimeout(function(){
// rebuild parsers.
me.config.parsers = buildParserCache(
me, $headers);
me.config.parsers = buildParserCache(me, $headers);
// rebuild the cache map
cache = buildCache(me);
}, 1);
@ -673,8 +703,7 @@
// get position from the dom.
pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex];
// update cache
cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format(
getElementText(config, cell), cell);
cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format(getElementText(config, cell), cell);
})
.bind("sorton", function(e, list) {
$(this).trigger("sortStart", tbl[0]);
@ -723,16 +752,13 @@
this.addWidget = function (widget) {
widgets.push(widget);
};
this.formatFloat = function (s) {
this.formatFloat = function(s) {
var i = parseFloat(s);
return (isNaN(i)) ? 0 : i;
// return the text instead of zero
return isNaN(i) ? $.trim(s) : i;
};
this.formatInt = function (s) {
var i = parseInt(s, 10);
return (isNaN(i)) ? 0 : i;
};
this.isDigit = function (s, config) {
// replace all an wanted chars and match.
this.isDigit = function(s) {
// replace all unwanted chars and match.
return (/^[\-+]?\d*$/).test($.trim(s.replace(/[,.']/g, '')));
};
this.clearTableBody = function (table) {
@ -772,12 +798,11 @@
ts.addParser({
id: "digit",
is: function(s, table){
var c = table.config;
return $.tablesorter.isDigit(s.replace(/,/g, ""), c); // isDigit(s, c);
is: function(s){
return $.tablesorter.isDigit(s.replace(/,/g, ""));
},
format: function(s){
return $.tablesorter.formatFloat(s.replace(/,/g, "")); // formatFloat(s);
return $.tablesorter.formatFloat(s.replace(/,/g, ""));
},
type: "numeric"
});
@ -788,7 +813,7 @@
return (/^[£$€¤¥¢?.]/).test(s);
},
format: function(s){
return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.\-]/g), "")); // RegExp(/[£$€]/g), ""));
return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.\-]/g), ""));
},
type: "numeric"
});
@ -831,9 +856,8 @@
is: function(s) {
return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/).test(s);
},
format: function (s) {
return $.tablesorter.formatFloat((s !== "") ? new Date(s.replace(
new RegExp(/-/g), "/")).getTime() : "0");
format: function(s) {
return $.tablesorter.formatFloat((s !== "") ? new Date(s.replace(new RegExp(/-/g), "/")).getTime() : "0");
},
type: "numeric"
});
@ -918,8 +942,7 @@
// loop through the visible rows
$("tr:visible", table.tBodies[0]).each(function (i) {
$tr = $(this);
// style children rows the same way the parent
// row was styled
// style children rows the same way the parent row was styled
if (!$tr.hasClass(table.config.cssChildRow)) { row++; }
odd = (row % 2 === 0);
$tr

File diff suppressed because one or more lines are too long