-
Notifications
You must be signed in to change notification settings - Fork 755
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
data-math="ignore" is not ignored (Math Widget) #1526
Comments
Hello, I would very much like to see this added to the widget. I am running into a similar problem. |
Hi @Sneezeplease! Do you mean you want the ability to add |
Yes... ignore a table "TD" cell . Please, please please. That's what I initially thought it was doing. I didn't realize, even after reviewing the documentation/sample, that it was a row-only attribute. The reason for it is because I'm averaging some data for a client, but some records don't have any response data. In those cases, I leave the table cell is empty (ie, null) but tablesorter auto-translates the missing value to "0" and then the averages are incorrect & unusable. (By contrast, MS Excel automatically ignores empty cells when performing math functions.) |
Sorry, I missed adding this enhancement in today's release. I'll make sure to include it in the next one. |
I just noticed that a new release became available 2 hours ago. I didn't see any change in behavior. In my sample (above), I'm expecting that the result of 3 populated cells w/100 be displayed as "100" instead of "60" as the two empty (non-zero) cells are ignored, . The v2.30.7 release is still including the ignored cells. (I tested count, mean or sum functions.) Was the enhancement included in the v2.30.7 release? If not, do you have a separate script I could try? Thanks. |
Sorry, I keep neglecting old issues... I'll really try to include this in the next update. |
I checked v2.31.0 (8/27/2018) and |
Sorry, no this enhancement wasn't included. I've been busy and neglecting work on this repo 😿. It is on my to-do list for the next update though! |
I saw that there's been a 2.31.1 update to the math widget on 11/20/2018. It doesn't look like this enhancement has been added yet. :( |
Sorry :( New baby, new job... I'm a bit overwhelmed. |
A project that I used this widget on just reported calculation errors due to empty non-numeric null-ish values being averaged as "0" and throwing off the final values. I've attempted to add support for cell-based "ignore", but not sure if it's correct or performant: |
Sorry for the long long delay on this... I modified your changes a bit. It appears to work, please test it out more thoroughly for me. I'll update the repo if it works well. widget-math.js/*! Widget: math - updated 11/18/2019 (v2.31.2) */ /*
* Requires tablesorter v2.16+ and jQuery 1.7+
* by Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
(function($) {
"use strict";
var ts = $.tablesorter,
math = {
error: {
0: "Infinity result: Divide by zero",
1: "Need more than one element to make this calculation",
undef: "No elements found"
},
// value returned when calculation is not possible, e.g. no values, dividing by zero, etc.
invalid: function(c, name, errorIndex) {
// name = function returning invalid results
// errorIndex = math.error index with an explanation of the error
console.warn(name, math.error[errorIndex]);
return (c && c.widgetOptions.math_none) || ""; // text for cell
},
events: "tablesorter-initialized update updateAll updateRows addRows updateCell filterReset "
.split(" ")
.join(".tsmath "),
processText: function(c, $cell) {
var tmp,
wo = c.widgetOptions,
txt = ts.getElementText(c, $cell, math.getCellIndex($cell)),
prefix = c.widgetOptions.math_prefix;
if (wo.math_textAttr) {
txt = $cell.attr(wo.math_textAttr) || txt;
}
if (/</.test(prefix)) {
// prefix contains HTML; remove it & any text before using formatFloat
tmp = $("<div>" + prefix + "</div>")
.text()
.replace(/\{content\}/g, "")
.trim();
txt = txt.replace(tmp, "");
}
txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), c.table) || 0;
// isNaN('') => false
return isNaN(txt) ? 0 : txt;
},
// get all of the row numerical values in an arry
getRow: function(c, $el, hasFilter) {
var $cells,
wo = c.widgetOptions,
arry = [],
$row = $el.closest("tr"),
isFiltered = $row.hasClass(wo.filter_filteredRow || "filtered");
if (hasFilter) {
$row = $row.filter(hasFilter);
}
if (hasFilter || !isFiltered) {
$cells = $row.children().not("[" + wo.math_dataAttrib + "=ignore]");
if (wo.math_ignore.length) {
$cells = $cells.filter(function() {
// using $.inArray is not optimal (needed for IE8)
return (
$.inArray(math.getCellIndex($(this)), wo.math_ignore) === -1
);
});
}
arry = $cells
.not($el)
.map(function() {
return math.processText(c, $(this));
})
.get();
}
return arry;
},
// get all of the column numerical values in an arry
getColumn: function(c, $el, type, hasFilter) {
var index,
$t,
$tr,
len,
$mathRows,
mathAbove,
wo = c.widgetOptions,
arry = [],
$row = $el.closest("tr"),
mathAttr = wo.math_dataAttrib,
mathIgnore = "[" + mathAttr + "=ignore]",
filtered = wo.filter_filteredRow || "filtered",
cIndex = math.getCellIndex($el),
// get all rows to keep row indexing
$rows = c.$table.children("tbody").children(),
mathAttrs = [
"[" + mathAttr + "^=above]",
"[" + mathAttr + "^=below]",
"[" + mathAttr + "^=col]",
"[" + mathAttr + "^=all]"
];
if (type === "above") {
len = $rows.index($row);
index = len;
while (index >= 0) {
$tr = $rows.eq(index);
mathAbove = $tr.children().filter(mathAttrs[0]).length;
if (hasFilter) {
$tr = $tr.filter(hasFilter);
}
$t = $tr.children().filter(function() {
return math.getCellIndex($(this)) === cIndex;
});
// ignore filtered rows & rows with data-math="ignore" (and starting row)
if (
((hasFilter || !$tr.hasClass(filtered)) &&
$tr.not(mathIgnore).length &&
index !== len) ||
(mathAbove && index !== len)
) {
// stop calculating 'above', when encountering another 'above'
if (mathAbove) {
index = 0;
} else if ($t.length && $t.not(mathIgnore).length) {
arry[arry.length] = math.processText(c, $t);
}
}
index--;
}
} else if (type === "below") {
len = $rows.length;
// index + 1 to ignore starting node
for (index = $rows.index($row) + 1; index < len; index++) {
$tr = $rows.eq(index);
if ($tr.children().filter(mathAttrs[1]).length) {
break;
}
if (hasFilter) {
$tr = $tr.filter(hasFilter);
}
$t = $tr.children().filter(function() {
return math.getCellIndex($(this)) === cIndex;
});
if (
(hasFilter || !$tr.hasClass(filtered)) &&
$tr.not(mathIgnore).length &&
$t.length &&
$t.not(mathIgnore)
) {
arry[arry.length] = math.processText(c, $t);
}
}
} else {
$mathRows = $rows.not(mathIgnore);
len = $mathRows.length;
for (index = 0; index < len; index++) {
$tr = $mathRows.eq(index);
if (hasFilter) {
$tr = $tr.filter(hasFilter);
}
$t = $tr.children().filter(function() {
return math.getCellIndex($(this)) === cIndex;
});
if (
(hasFilter || !$tr.hasClass(filtered)) &&
$t.not(mathAttrs.join(",")).length &&
!$t.is($el) &&
$t.not(mathIgnore).length
) {
arry[arry.length] = math.processText(c, $t);
}
}
}
return arry;
},
// get all of the column numerical values in an arry
getAll: function(c, hasFilter) {
var $t,
col,
$row,
rowIndex,
rowLen,
$cells,
cellIndex,
cellLen,
arry = [],
wo = c.widgetOptions,
mathAttr = wo.math_dataAttrib,
mathIgnore = "[" + mathAttr + "=ignore]",
filtered = wo.filter_filteredRow || "filtered",
$rows = c.$table.children("tbody").children().not(mathIgnore);
rowLen = $rows.length;
for (rowIndex = 0; rowIndex < rowLen; rowIndex++) {
$row = $rows.eq(rowIndex);
if (hasFilter) {
$row = $row.filter(hasFilter);
}
if (hasFilter || !$row.hasClass(filtered)) {
$cells = $row.children().not(mathIgnore);
cellLen = $cells.length;
for (cellIndex = 0; cellIndex < cellLen; cellIndex++) {
$t = $cells.eq(cellIndex);
col = math.getCellIndex($t);
if (
!$t.filter("[" + mathAttr + "]").length &&
$.inArray(col, wo.math_ignore) < 0
) {
arry[arry.length] = math.processText(c, $t);
}
}
}
}
return arry;
},
setColumnIndexes: function(c) {
var $table = c.$table,
last = 1,
// only target rows with a colspan or rows included in a rowspan
$rows = $table.children("tbody").children().filter(function() {
var cells,
indx,
$this = $(this),
include = $this.children("[colspan]").length > 0;
if (last > 1) {
last--;
include = true;
} else if (last < 1) {
last = 1;
}
if ($this.children("[rowspan]").length > 0) {
cells = this.cells;
// find max rowspan (in case more than one cell has a rowspan)
for (indx = 0; indx < cells.length; indx++) {
last = Math.max(cells[indx].rowSpan, last);
}
}
return include;
});
// pass `c` (table.config) to computeColumnIndex so it won't add a data-column
// to every tbody cell, just the ones where the .cellIndex property doesn't match
// the calculated cell index - hopefully fixes the lag issue in #1048
ts.computeColumnIndex($rows, c);
},
getCellIndex: function($cell) {
var indx = $cell.attr("data-column");
return typeof indx === "undefined"
? $cell[0].cellIndex
: parseInt(indx, 10);
},
recalculate: function(c, wo, init) {
if (c && (!wo.math_isUpdating || init)) {
var time,
mathAttr,
$mathCells,
indx,
len,
changed = false,
filters = {};
if (c.debug || wo.math_debug) {
time = new Date();
}
// add data-column attributes to all table cells
if (init) {
math.setColumnIndexes(c);
}
// data-attribute name (defaults to data-math)
wo.math_dataAttrib = "data-" + (wo.math_data || "math");
// all non-info tbody cells
mathAttr = wo.math_dataAttrib;
$mathCells = c.$tbodies.children("tr").children("[" + mathAttr + "]");
changed = math.mathType(c, $mathCells, wo.math_priority) || changed;
// only info tbody cells
$mathCells = c.$table
.children("." + c.cssInfoBlock + ", tfoot")
.children("tr")
.children("[" + mathAttr + "]");
math.mathType(c, $mathCells, wo.math_priority);
// find the 'all' total
$mathCells = c.$table
.children()
.children("tr")
.children("[" + mathAttr + "^=all]");
len = $mathCells.length;
// get math filter, if any
// hasFilter = $row.attr( mathAttr + '-filter' ) || wo.math_rowFilter;
for (indx = 0; indx < len; indx++) {
var $cell = $mathCells.eq(indx),
filter = $cell.attr(mathAttr + "-filter") || wo.math_rowFilter;
filters[filter] = filters[filter]
? filters[filter].add($cell)
: $cell;
}
$.each(filters, function(hasFilter, $cells) {
changed = math.mathType(c, $cells, ["all"], hasFilter) || changed;
});
// trigger an update only if cells inside the tbody changed
if (changed) {
wo.math_isUpdating = true;
if (c.debug || wo.math_debug) {
console[console.group ? "group" : "log"](
"Math widget updating the cache after recalculation"
);
}
// update internal cache, but ignore "remove-me" rows and do not resort
ts.updateCache(c, function() {
math.updateComplete(c);
if (!init && typeof wo.math_completed === "function") {
wo.math_completed(c);
}
if (c.debug || wo.math_debug) {
console.log(
"Math widget update completed" + ts.benchmark(time)
);
}
});
} else {
if (!init && typeof wo.math_completed === "function") {
wo.math_completed(c);
}
if (c.debug || wo.math_debug) {
console.log(
"Math widget found no changes in data" + ts.benchmark(time)
);
}
}
}
},
updateComplete: function(c) {
var wo = c.widgetOptions;
if (
wo.math_isUpdating &&
(c.debug || wo.math_debug) &&
console.groupEnd
) {
console.groupEnd();
}
wo.math_isUpdating = false;
},
mathType: function(c, $cells, priority, hasFilter) {
if ($cells.length) {
var getAll,
changed = false,
wo = c.widgetOptions,
mathAttr = wo.math_dataAttrib,
equations = ts.equations;
if (priority[0] === "all") {
// mathType is called multiple times if more than one "hasFilter" is used
getAll = math.getAll(c, hasFilter);
}
if (c.debug || wo.math_debug) {
console[console.group ? "group" : "log"](
"Tablesorter Math widget recalculation"
);
}
// $.each is okay here... only 4 priorities
$.each(priority, function(i, type) {
var index,
arry,
formula,
result,
$el,
$targetCells = $cells.filter("[" + mathAttr + "^=" + type + "]"),
len = $targetCells.length;
if (len) {
if (c.debug || wo.math_debug) {
console[console.group ? "group" : "log"](type);
}
for (index = 0; index < len; index++) {
$el = $targetCells.eq(index);
// Row is filtered, no need to do further checking
if (
$el.parent().hasClass(wo.filter_filteredRow || "filtered")
) {
continue;
}
hasFilter = $el.attr(mathAttr + "-filter") || wo.math_rowFilter;
formula = ($el.attr(mathAttr) || "").replace(type + "-", "");
arry =
type === "row"
? math.getRow(c, $el, hasFilter)
: type === "all"
? getAll
: math.getColumn(c, $el, type, hasFilter);
if (equations[formula]) {
if (arry.length) {
result = equations[formula](arry, c);
if (c.debug || wo.math_debug) {
console.log(
$el.attr(mathAttr),
hasFilter ? '("' + hasFilter + '")' : "",
arry,
"=",
result
);
}
} else {
// mean will return a divide by zero error, everything else shows an undefined error
result = math.invalid(
c,
formula,
formula === "mean" ? 0 : "undef"
);
}
changed = math.output($el, c, result, arry) || changed;
}
}
if ((c.debug || wo.math_debug) && console.groupEnd) {
console.groupEnd();
}
}
});
if ((c.debug || wo.math_debug) && console.groupEnd) {
console.groupEnd();
}
return changed;
}
return false;
},
output: function($cell, c, value, arry) {
// get mask from cell data-attribute: data-math-mask="#,##0.00"
var $el,
wo = c.widgetOptions,
changed = false,
prev = $cell.html(),
mask = $cell.attr("data-" + wo.math_data + "-mask") || wo.math_mask,
target = $cell.attr("data-" + wo.math_data + "-target") || "",
result = ts.formatMask(mask, value, wo.math_prefix, wo.math_suffix);
if (target) {
$el = $cell.find(target);
if ($el.length) {
$cell = $el;
}
}
if (typeof wo.math_complete === "function") {
result = wo.math_complete($cell, wo, result, value, arry);
}
if (result !== false) {
changed = prev !== result;
$cell.html(result);
}
// check if in a regular tbody, otherwise don't pass a changed flag
// to prevent unnecessary updating of the table cache
if (changed) {
$el = $cell.closest("tbody");
// content was changed in a tfoot, info-only tbody or the resulting tbody is in a nested table
// then don't signal a change
if (
!$el.length ||
$el.hasClass(c.cssInfoBlock) ||
$el.parent()[0] !== c.table
) {
return false;
}
}
return changed;
}
};
// Modified from https://code.google.com/p/javascript-number-formatter/
/**
* @preserve IntegraXor Web SCADA - JavaScript Number Formatter
* http:// www.integraxor.com/
* author: KPL, KHL
* (c)2011 ecava
* Dual licensed under the MIT or GPL Version 2 licenses.
*/
ts.formatMask = function(mask, val, tmpPrefix, tmpSuffix) {
if (!mask || isNaN(+val)) {
return val; // return as it is.
}
var isNegative,
result,
decimal,
group,
posLeadZero,
posTrailZero,
posSeparator,
part,
szSep,
integer,
str,
offset,
index,
end,
inv,
suffix = "",
// find prefix/suffix
len = mask.length,
start = mask.search(/[0-9\-\+#]/),
tmp = start > 0 ? mask.substring(0, start) : "",
prefix = tmp;
if (tmpPrefix) {
if (/\{content\}/.test(tmpPrefix || "")) {
prefix = (tmpPrefix || "").replace(/\{content\}/g, tmp || "");
} else {
prefix = (tmpPrefix || "") + tmp;
}
}
// reverse string: not an ideal method if there are surrogate pairs
inv = mask.split("").reverse().join("");
end = inv.search(/[0-9\-\+#]/);
index = len - end;
index += mask.substring(index, index + 1) === "." ? 1 : 0;
tmp = end > 0 ? mask.substring(index, len) : "";
suffix = tmp;
if (tmpSuffix) {
if (/\{content\}/.test(tmpSuffix || "")) {
suffix = (tmpSuffix || "").replace(/\{content\}/g, tmp || "");
} else {
suffix = tmp + (tmpSuffix || "");
}
}
mask = mask.substring(start, index);
// convert any string to number according to formation sign.
val = mask.charAt(0) === "-" ? -val : +val;
isNegative = val < 0 ? (val = -val) : 0; // process only abs(), and turn on flag.
// search for separator for grp & decimal, anything not digit, not +/- sign, not #.
result = mask.match(/[^\d\-\+#]/g);
decimal = (result && result[result.length - 1]) || "."; // treat the right most symbol as decimal
group = (result && result[1] && result[0]) || ","; // treat the left most symbol as group separator
// split the decimal for the format string if any.
mask = mask.split(decimal);
// Fix the decimal first, toFixed will auto fill trailing zero.
val = val.toFixed(mask[1] && mask[1].length);
val = +val + ""; // convert number to string to trim off *all* trailing decimal zero(es)
// fill back any trailing zero according to format
posTrailZero = mask[1] && mask[1].lastIndexOf("0"); // look for last zero in format
part = val.split(".");
// integer will get !part[1]
if (!part[1] || (part[1] && part[1].length <= posTrailZero)) {
val = (+val).toFixed(posTrailZero + 1);
}
szSep = mask[0].split(group); // look for separator
mask[0] = szSep.join(""); // join back without separator for counting the pos of any leading 0.
posLeadZero = mask[0] && mask[0].indexOf("0");
if (posLeadZero > -1) {
while (part[0].length < mask[0].length - posLeadZero) {
part[0] = "0" + part[0];
}
} else if (+part[0] === 0) {
part[0] = "";
}
val = val.split(".");
val[0] = part[0];
// process the first group separator from decimal (.) only, the rest ignore.
// get the length of the last slice of split result.
posSeparator = szSep[1] && szSep[szSep.length - 1].length;
if (posSeparator) {
integer = val[0];
str = "";
offset = integer.length % posSeparator;
len = integer.length;
for (index = 0; index < len; index++) {
str += integer.charAt(index); // ie6 only support charAt for sz.
// -posSeparator so that won't trail separator on full length
/*jshint -W018 */
if (
!((index - offset + 1) % posSeparator) &&
index < len - posSeparator
) {
str += group;
}
}
val[0] = str;
}
val[1] = mask[1] && val[1] ? decimal + val[1] : "";
// put back any negation, combine integer and fraction, and add back prefix & suffix
return prefix + ((isNegative ? "-" : "") + val[0] + val[1]) + suffix;
};
ts.equations = {
count: function(arry) {
return arry.length;
},
sum: function(arry) {
var index,
len = arry.length,
total = 0;
for (index = 0; index < len; index++) {
total += arry[index];
}
return total;
},
mean: function(arry) {
var total = ts.equations.sum(arry);
return total / arry.length;
},
median: function(arry, c) {
var half,
len = arry.length;
if (len > 1) {
// https://gist.github.com/caseyjustus/1166258
arry.sort(function(a, b) {
return a - b;
});
half = Math.floor(len / 2);
return len % 2 ? arry[half] : (arry[half - 1] + arry[half]) / 2;
}
return math.invalid(c, "median", 1);
},
mode: function(arry) {
// http://stackoverflow.com/a/3451640/145346
var index,
el,
m,
modeMap = {},
maxCount = 1,
modes = [arry[0]];
for (index = 0; index < arry.length; index++) {
el = arry[index];
modeMap[el] = modeMap[el] ? modeMap[el] + 1 : 1;
m = modeMap[el];
if (m > maxCount) {
modes = [el];
maxCount = m;
} else if (m === maxCount) {
modes[modes.length] = el;
maxCount = m;
}
}
// returns arry of modes if there is a tie
return modes.sort(function(a, b) {
return a - b;
});
},
max: function(arry) {
return Math.max.apply(Math, arry);
},
min: function(arry) {
return Math.min.apply(Math, arry);
},
range: function(arry) {
var v = arry.sort(function(a, b) {
return a - b;
});
return v[arry.length - 1] - v[0];
},
// common variance equation
// (not accessible via data-attribute setting)
variance: function(arry, population, c) {
var divisor,
avg = ts.equations.mean(arry),
v = 0,
i = arry.length;
while (i--) {
v += Math.pow(arry[i] - avg, 2);
}
divisor = arry.length - (population ? 0 : 1);
if (divisor === 0) {
return math.invalid(c, "variance", 0);
}
v /= divisor;
return v;
},
// variance (population)
varp: function(arry, c) {
return ts.equations.variance(arry, true, c);
},
// variance (sample)
vars: function(arry, c) {
return ts.equations.variance(arry, false, c);
},
// standard deviation (sample)
stdevs: function(arry, c) {
var vars = ts.equations.variance(arry, false, c);
return Math.sqrt(vars);
},
// standard deviation (population)
stdevp: function(arry, c) {
var varp = ts.equations.variance(arry, true, c);
return Math.sqrt(varp);
}
};
// add new widget called repeatHeaders
// ************************************
ts.addWidget({
id: "math",
priority: 100,
options: {
math_data: "math",
math_debug: false,
// column index to ignore
math_ignore: [],
// mask info: https://code.google.com/p/javascript-number-formatter/
math_mask: "#,##0.00",
// complete executed after each fucntion
math_complete: null, // function($cell, wo, result, value, arry) { return result; },
// math_completed called after all math calculations have completed
math_completed: function(/* config */) {},
// order of calculation; 'all' is last
math_priority: ["row", "above", "below", "col"],
// template for or just prepend the mask prefix & suffix with this HTML
// e.g. '<span class="red">{content}</span>'
math_prefix: "",
math_suffix: "",
// cell attribute containing the math value to use
math_textAttr: "",
// no matching math elements found (text added to cell)
math_none: "N/A",
math_event: "recalculate",
// use this filter to target specific rows (e.g. ':visible', or ':not(.empty-row)')
math_rowFilter: ""
},
init: function(table, thisWidget, c, wo) {
// filterEnd fires after updateComplete
var update =
(ts.hasWidget(table, "filter") ? "filterEnd" : "updateComplete") +
".tsmath";
// filterEnd is when the pager hides rows... so bind to pagerComplete
math.events +=
(ts.hasWidget(table, "pager") ? "pagerComplete" : "filterEnd") +
".tsmath ";
c.$table
.off(
(math.events + "updateComplete.tsmath " + wo.math_event).replace(
/\s+/g,
" "
)
)
.on(math.events + wo.math_event, function(e) {
if (!this.hasInitialized) {
return;
}
var init = e.type === "tablesorter-initialized";
if (!wo.math_isUpdating || init) {
// don't setColumnIndexes on init here, or it gets done twice
if (!/filter/.test(e.type) && !init) {
// redo data-column indexes on update
math.setColumnIndexes(c);
}
math.recalculate(c, wo, init);
}
})
.on(update, function() {
setTimeout(function() {
math.updateComplete(c);
}, 40);
});
wo.math_isUpdating = false;
// math widget initialized after table - see #946
if (table.hasInitialized) {
math.recalculate(c, wo, true);
}
},
// this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin
// this function only applies to tablesorter v2.4+
remove: function(table, c, wo, refreshing) {
if (refreshing) {
return;
}
c.$table
.off(
(math.events + " updateComplete.tsmath " + wo.math_event).replace(
/\s+/g,
" "
)
)
.children()
.children("tr")
.children("[data-" + wo.math_data + "]")
.empty();
}
});
})(jQuery); |
Thanks! This "mathIgnore" update provides me with the correct column-based values when generically tested against count, mean & sum. I noticed that quotes changed from 'single' to "double". I'm curious... is this a coding practice change or just the effect of using a different IDE/editor? |
When I post code here in a GitHub comment, prettier-github bot automatically reformats the code to use double quotes. |
Hey Mottie, grats on the new baby and job. (though not so new anymore...) We just ran into this bug, same as OP: some columns have no data, and median and average are affected by |
Hi @VaelVictus! Thanks! I'll try to get a new release out this weekend some time. Happy Thanksgiving! |
Sorry for the super-long delay! Version 2.31.2 is now available. |
Just tested it; works perfectly, thank you very much! |
This was what I've been looking for, didn't find it in the docs, only here. |
Hi @szabizs! It's in the docs here - https://mottie.github.io/tablesorter/docs/example-widget-math.html#attribute_settings - under "Ignoring cells". Please let me know if that is what you're referring to, or if there is something missing in that section so I can improve it. Thanks! |
@Mottie sorry for the confusion, I've searched for the work skip rather than ignore, that's why I wasn't able to find it 👍 |
I'm generating a table with numeric values, but some records don't have any data. In this case, I'm not outputting any value and the
TD
element is empty. I noticed that the math widget was converting these empty values to "0" when averaging, so I addeddata-math="ignore"
to these emptyTD
elements. The cells are still being converted to "0" and included in the average.For example, the mean of non-ignored fields should be "100". The value "60" is returned, so this must mean that it's evaluating ignored cells as "0" instead of ignoring them. (By comparison, if I create 5 cells in Excel and only 3 are populated w/100, the average is "100".)
The console outputs:
Am I missing something?
The text was updated successfully, but these errors were encountered: