I have table data for UP and DOWN counts and I want to calculate and display "uptime" as a percentage, for several targets within a the same table. Source counts have commas that must be removed, and I need to show the results to 3 decimal places (no rounding). Some targets begin with a number. The table itself is from an external source, displayed in an iFrame.
So far I have got the decimal places truncating, but I am looking for help parsing the table columns and rows to remove the commas and do the math.
(100 - (downCount / upCount * 100))
EDIT: The accepted answer changed this to:
(upCount / (upCount + downCount) * 100)
https://jsfiddle.net/ncarlton/d12ec7q3/1/
Vanilla JavaSript only please (no jQuery).
JavaScript Starter:
// these are the "correct" answers I'm trying to calculate from sourceTable data
let firstTargetCalc = (100 - (42 / 2218563 * 100));
let _2ndTargetCalc = (100 - (3 / 239384 * 100));
let thirdTargetCalc = (100 - (0 / 8340 * 100));
// this is how I want to truncate the decimals -- not rounding
function truncateDecimals(num, digits) {
var numS = num.toString(),
decPos = numS.indexOf('.'),
substrLength = decPos == -1 ? numS.length : 1 + decPos + digits,
trimmedResult = numS.substr(0, substrLength),
finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;
return parseFloat(finalResult);
}
// here's the "final" answers with the truncation
let trunc1st = truncateDecimals(firstTargetCalc, 4)
let trunc2nd = truncateDecimals(_2ndTargetCalc, 4)
let trunc3rd = truncateDecimals(thirdTargetCalc, 4)
// here's what I want to display to the end user as the calculated uptime for each target
// alert('firsttarget uptime: ' + trunc1st + '%\n2ndtarget uptime: ' + trunc2nd + '%\nthirdtarget uptime: ' + trunc3rd + '%');
The full HTML with a large extraneous <thead> is in the JSFiddle. But the important part might look like this in the <tbody> for three targets. The number of targets will vary but will usually be fewer than ten.
HTML:
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">firsttarget</td>
<td>UP</td>
<td class="numeric-value">2,218,563</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">firsttarget</td>
<td>DOWN</td>
<td class="numeric-value">42</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">2ndtarget</td>
<td>UP</td>
<td class="numeric-value">239,384</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">2ndtarget</td>
<td>DOWN</td>
<td class="numeric-value">3</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">thirdtarget</td>
<td>UP</td>
<td class="numeric-value">8,340</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">thirdtarget</td>
<td>DOWN</td>
<td class="numeric-value">0</td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
</tbody>```
[1]: https://i.stack.imgur.com/SNDdW.png
You can use regex to parse your table rather than iterating rows; your calculation to get the percentage is also incorrect. See snippet for working example.
function showTableData() {
const targetTable = document.getElementById('jstable'); // where to draw the new table
const sourceTable = document.querySelector(".table"); // set kibana as
const innerText = sourceTable.innerText;
const ups = innerText.match(/(\w*(target))\s*(UP)\s*[\d,]*\d(?! )/g);
const downs = innerText.match(/(\w*(target))\s*(DOWN)\s*[\d,]*\d(?! )/g);
let innerHTML = "";
for (let i = 0; i < ups.length; i++) {
const up = ups[i];
const down = downs[i];
const target = up.match(/(\w*(target))/)[0];
const upCount = parseInt(up.match(/[\d,]*\d$/)[0].replace(/,/g, ""));
const downCount = parseInt(down.match(/[\d,]*\d$/)[0].replace(/,/g, ""));
const percentage = (upCount / (upCount + downCount) * 100).toFixed(3)
innerHTML += `${target} uptime: ${percentage}%<br/><br/>`
}
targetTable.innerHTML = innerHTML;
}
// these are the "correct" answers I'm trying to calculate from sourceTable data
let firstTargetCalc = (100 - (42 / 2218563 * 100));
let _2ndTargetCalc = (100 - (3 / 239384 * 100));
let thirdTargetCalc = (100 - (0 / 8340 * 100));
// this is how I want to truncate the decimals -- not rounding
function truncateDecimals(num, digits) {
var numS = num.toString(),
decPos = numS.indexOf('.'),
substrLength = decPos == -1 ? numS.length : 1 + decPos + digits,
trimmedResult = numS.substr(0, substrLength),
finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;
return parseFloat(finalResult);
}
// here's the "final" answers with the truncation
let trunc1st = truncateDecimals(firstTargetCalc, 4)
let trunc2nd = truncateDecimals(_2ndTargetCalc, 4)
let trunc3rd = truncateDecimals(thirdTargetCalc, 4)
// here's what I want to display to the end user as the calculated uptime for each target
// alert('firsttarget uptime: ' + trunc1st + '%\n2ndtarget uptime: ' + trunc2nd + '%\nthirdtarget uptime: ' + trunc3rd + '%');
// this will eventually be run within the function to set the iFrame URL
// need to wait for the iFrame to load to get data for calculations
// for now we will present a button...
function showTableData() {
const targetTable = document.getElementById('jstable'); // where to draw the new table
const sourceTable = document.querySelector(".table"); // set kibana as
const innerText = sourceTable.innerText;
const ups = innerText.match(/(\w*(target))\s*(UP)\s*[\d,]*\d(?! )/g);
const downs = innerText.match(/(\w*(target))\s*(DOWN)\s*[\d,]*\d(?! )/g);
let innerHTML = "";
for (let i = 0; i < ups.length; i++) {
const up = ups[i];
const down = downs[i];
const target = up.match(/(\w*(target))/)[0];
const upCount = parseInt(up.match(/[\d,]*\d$/)[0].replace(/,/g, ""));
const downCount = parseInt(down.match(/[\d,]*\d$/)[0].replace(/,/g, ""));
const percentage = (upCount / (upCount + downCount) * 100).toFixed(3)
innerHTML += `${target} uptime: ${percentage}%<br/><br/>`
}
targetTable.innerHTML = innerHTML;
}
/*
function showTableData() {
document.getElementById('jstable').innerHTML = ""; // where to draw the new table
let sourceTable = document.querySelector(".table"); // set "source" table
for (var i = 0, row; row = sourceTable.rows[i]; i++) {
//iterate through rows
//rows would be accessed using the "row" variable assigned in the for loop
for (var j = 0, col; col = row.cells[j]; j++) {
//iterate through columns
//columns would be accessed using the "col" variable assigned in the for loop
}
}
}
/*calc.prototype.toFixedDown = function(digits) {
var re = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)"),
m = this.toString().match(re);
return m ? parseFloat(m[1]) : this.valueOf();
};*/
/*
// rounding function
function roundTo(n, digits) {
var negative = false;
if (digits === undefined) {
digits = 0;
}
if( n < 0) {
negative = true;
n = n * -1;
}
var multiplicator = Math.pow(10, digits);
n = parseFloat((n * multiplicator).toFixed(11));
n = (Math.round(n) / multiplicator).toFixed(2);
if( negative ) {
n = (n * -1).toFixed(2);
}
return n;
}
// set the vars
/*let uptimeCells = document.querySelectorAll("td");
for (let i = 0; i < uptimeCells.length; i+=3) {
let uptimeTarget = uptimeCells[i].firstChild.data;
let uptimeFilter = uptimeCells[i + 1].firstChild.data;
let uptimeCount = uptimeCells[i + 2].firstChild.data;
console.log(uptimeTarget, uptimeFilter, uptimeCount);
}
// if () {
// }
return `<p>${this.targetName} ${this.calculatedUptime}</p>`;*/
<body>
<div class="rebuild-table">
<p><input type="button" id="bt" value="Show Rebuilt Table" onclick="showTableData()" /></p>
<p id="jstable"></p>
<h1>
Expected Results
</h1>
<div id=results>
<p>
firsttarget uptime: 99.998%
<!--(100-((42/2218563)*100))-->
</p>
<p>
2ndtarget uptime: 99.999%
<!--(100-((3/239384)*100)) rounded up from 99.9987..-->
</p>
<p>
thirdtarget uptime: 100.000%
<!--(100-((0/8340)*100))-->
</p>
</div>
</div>
<div>
<h1>
Data
</h1>
<!--THE TABLE BELOW IS PRESENTED WITHIN AN IFRAME BUT FOR THE JSFIDDLEWE WILL COMMENT OUT THE IFRAME ELEMENT
<iframe id="iframeURL">-->
<table class="table table-condensed">
<thead>
<tr>
<!-- ngRepeat: col in ::columns -->
<th ng-repeat="col in ::columns" ng-click="paginatedTable.sortColumn($index)" class="">
<span ng-bind="::col.title" class="ng-binding">targets</span>
<!-- ngIf: col.info -->
<!-- ngIf: col.sortable !== false --><i ng-if="col.sortable !== false" class="fa ng-scope fa-sort" ng-class="{
'fa-sort-asc': paginatedTable.sort.columnIndex === $index && paginatedTable.sort.direction === 'asc',
'fa-sort-desc': paginatedTable.sort.columnIndex === $index && paginatedTable.sort.direction === 'desc',
'fa-sort': paginatedTable.sort.columnIndex !== $index || paginatedTable.sort.direction === null
}">
</i>
<!-- end ngIf: col.sortable !== false -->
<!-- ngIf: col.filterable --><i aria-label="Click on a cell to filter" ng-if="col.filterable" class="fa fa-search ng-scope" ng-click="$event.stopPropagation()" tooltip="Click on a cell to filter"></i>
<!-- end ngIf: col.filterable -->
</th>
<!-- end ngRepeat: col in ::columns -->
<th ng-repeat="col in ::columns" ng-click="paginatedTable.sortColumn($index)" class="">
<span ng-bind="::col.title" class="ng-binding">filters</span>
<!-- ngIf: col.info -->
<!-- ngIf: col.sortable !== false --><i ng-if="col.sortable !== false" class="fa ng-scope fa-sort" ng-class="{
'fa-sort-asc': paginatedTable.sort.columnIndex === $index && paginatedTable.sort.direction === 'asc',
'fa-sort-desc': paginatedTable.sort.columnIndex === $index && paginatedTable.sort.direction === 'desc',
'fa-sort': paginatedTable.sort.columnIndex !== $index || paginatedTable.sort.direction === null
}">
</i>
<!-- end ngIf: col.sortable !== false -->
<!-- ngIf: col.filterable -->
</th>
<!-- end ngRepeat: col in ::columns -->
<th ng-repeat="col in ::columns" ng-click="paginatedTable.sortColumn($index)" class="visualize-table-right">
<span ng-bind="::col.title" class="ng-binding">Count</span>
<!-- ngIf: col.info -->
<!-- ngIf: col.sortable !== false --><i ng-if="col.sortable !== false" class="fa ng-scope fa-sort" ng-class="{
'fa-sort-asc': paginatedTable.sort.columnIndex === $index && paginatedTable.sort.direction === 'asc',
'fa-sort-desc': paginatedTable.sort.columnIndex === $index && paginatedTable.sort.direction === 'desc',
'fa-sort': paginatedTable.sort.columnIndex !== $index || paginatedTable.sort.direction === null
}">
</i>
<!-- end ngIf: col.sortable !== false -->
<!-- ngIf: col.filterable -->
</th>
<!-- end ngRepeat: col in ::columns -->
</tr>
</thead>
<tbody kbn-rows="page" kbn-rows-min="perPage">
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">firsttarget</td>
<td>UP</td>
<td class="numeric-value">2,218,563</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">firsttarget</td>
<td>DOWN</td>
<td class="numeric-value">42</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">2ndtarget</td>
<td>UP</td>
<td class="numeric-value">239,384</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">2ndtarget</td>
<td>DOWN</td>
<td class="numeric-value">3</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">thirdtarget</td>
<td>UP</td>
<td class="numeric-value">8,340</td>
</tr>
<tr>
<td class="cell-hover ng-scope" ng-click="clickHandler($event)">thirdtarget</td>
<td>DOWN</td>
<td class="numeric-value">0</td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
</div>
<!--</iframe>-->
<p>
Notes: some targets begin with numbers. The final implementation will have various numbers of targets to calculate. Cannot change the table itself, other than the names of the UP and DOWN filters (e.g., cannot add id="" to any table elements because it's
generated externally). The entire 'thead' element is not important to this project.</p>
</body>
Related
I'm trying to add commas to my output answers in JavaScript calculator. I updated an old calculator that was used on a previous website that I maintained and it's working, but I can't get the answers to show up with the comma. I was excited I figured out how to round to 2 decimal places. I've tried a few different things that I feel like might have worked, but I was missing something in how to change the code since my code is set up differently than the examples I saw.
function clear_buildingCalc() {
document.buildingCalc.ICCVal.value = "";
document.buildingCalc.permitFee.value = "";
document.buildingCalc.planReview.value = "";
document.buildingCalc.Subtotal.value = "";
document.buildingCalc.DBPR.value = "";
document.buildingCalc.DCA.value = "";
document.buildingCalc.totalFee.value = "";
document.buildingCalc.Subtotaldisc.value = "";
document.buildingCalc.DBPRdisc.value = "";
document.buildingCalc.DCAdisc.value = "";
document.buildingCalc.totalFeedisc.value = "";
}
function calcBuilding(ICCVal) {
var validNums = "0123456789"
var flag = "yes";
var tempChar;
for (var c = 0; c < ICCVal.value.length; c++) {
tempChar = "" + ICCVal.value.substring(c, c + 1);
if (validNums.indexOf(tempChar) == "-1") {
flag = "no";
}
}
if (flag == "no") {
alert("Please enter only whole numbers.");
}
if (flag == "yes") {
if (document.buildingCalc.ICCVal.value == "") {
alert("Please enter the ICC valuation.");
} else {
var addlFee = 5.26;
var baseFee = 968.50;
var roundICC = Math.round(ICCVal.value - 100000) / 1000;
var permitFee = Math.round((addlFee * roundICC) * 1000) / 1000;
var permitFee2 = Math.round((permitFee + baseFee) * 100) / 100;
var planReviewRate = 0.25;
var planReview = Math.round((planReviewRate * permitFee2) * 1000) / 1000;
var Subtotal = Math.round((permitFee2 + planReview) * 100) / 100;
var DBPRRate = 0.015;
var DBPR = Math.round((DBPRRate * Subtotal) * 100) / 100;
var DCARate = 0.01;
var DCA = Math.round((DCARate * Subtotal) * 100) / 100;
var totalFee = Math.round((Subtotal + DBPR + DCA) * 100) / 100;
var Subtotaldisc = Math.round((Subtotal * 0.25) * 100) / 100;
var DBPRRatedisc = 0.015;
var DBPRdisc = Math.round((DBPRRatedisc * Subtotaldisc) * 100) / 100;
var DCARatedisc = 0.01;
var DCAdisc = Math.round((DCARatedisc * Subtotaldisc) * 100) / 100;
var totalFeedisc = Math.round((Subtotaldisc + DBPRdisc + DCAdisc) * 100) / 100;
document.buildingCalc.permitFee.value = permitFee2;
document.buildingCalc.planReview.value = planReview;
document.buildingCalc.Subtotal.value = Subtotal;
document.buildingCalc.DBPR.value = DBPR;
document.buildingCalc.DCA.value = DCA;
document.buildingCalc.totalFee.value = totalFee;
document.buildingCalc.Subtotaldisc.value = Subtotaldisc;
document.buildingCalc.DBPRdisc.value = DBPRdisc;
document.buildingCalc.DCAdisc.value = DCAdisc;
document.buildingCalc.totalFeedisc.value = totalFeedisc;
}
}
}
<form name="buildingCalc">
<table border="0" cellpadding="5" bordercolor="#000000">
<tr>
<td width="350" valign="bottom"><span class="body">Enter the ICC valuation (round to the nearest thousand) Example: 342778.20 is 343000 </span></td>
<td align="center" valign="bottom">
<div class="body">$<input type="text" name="ICCVal">
</div>
</td>
</tr>
<tr>
<td colspan="2">
<span class="body"><input type="button" onClick="calcBuilding(ICCVal)" value="Calculate building permit fee"></span>
</td>
</tr>
<tr>
<td>
<div class="body">Building permit fee</div>
</td>
<td align="center" class="body">$<input type="text" name="permitFee">
</td>
</tr>
<tr>
<td>
<div class="body">Plan review 25%</div>
</td>
<td class="body">$<input type="text" name="planReview">
</td>
</tr>
<tr>
<td>
<div class="body">Subtotal</div>
</td>
<td class="body">$<input type="text" name="Subtotal">
</td>
</tr>
<tr>
<td>
<div class="body">DBPR 1.5%</div>
</td>
<td class="body">$<input type="text" name="DBPR">
</td>
</tr>
<tr>
<td>
<div class="body">DCA 1%</div>
</td>
<td class="body">$<input type="text" name="DCA">
</td>
</tr>
<tr>
<td>
<div class="body">Total building permit fee</div>
</td>
<td class="body">$<input type="text" name="totalFee">
</td>
</tr>
<tr>
<td colspan="2" class="body">
<p><strong> <label>Does not include impact fees (if applicable), or Fire fees for commercial permits.</label>
</strong></p>
<p class="subpagesubheader">Fees effective Jan. 1, 2021 with 75% reduction applied </p>
</td>
</tr>
<tr>
<td>
<div class="body">Subtotal</div>
</td>
<td class="body">$<input type="text" name="Subtotaldisc">
</td>
</tr>
<tr>
<td>
<div class="body">DBPR 1.5%</div>
</td>
<td class="body">$<input type="text" name="DBPRdisc">
</td>
</tr>
<tr>
<td>
<div class="body">DCA 1%</div>
</td>
<td class="body">$<input type="text" name="DCAdisc">
</td>
</tr>
<tr>
<td>
<div class="body">Total building permit fee</div>
</td>
<td class="body">$<input type="text" `enter code here`name="totalFeedisc">
</td>
</tr>
<tr>
<td colspan="2" class="body">
<p><input type="button" onClick="clear_buildingCalc()" value="Clear calculation"></p>
</table>
</form>
Javascript
<script language="JavaScript">
function clear_ICCCalc()
{
document.ICCCalc.OccupancyConst.value = "";
document.ICCCalc.SquareFeet.value = "";
document.getElementById("result").innerHTML = "";
}
function multiplyBy()
{
num1 = document.getElementById("OccupancyConst").value;
num2 = document.getElementById("SquareFeet").value;
document.getElementById("result").innerHTML = num1 * num2;
}
</script>
You can use .toLocaleString(); when assigning the values. For e.g
document.buildingCalc.totalFeedisc.value = totalFeedisc.toLocaleString()
If you want decimal to work include . inside var validNums = "0123456789."
That way you can keep the functionality & change anytime you want
function clear_buildingCalc() {
document.buildingCalc.ICCVal.value = "";
document.buildingCalc.permitFee.value = "";
document.buildingCalc.planReview.value = "";
document.buildingCalc.Subtotal.value = "";
document.buildingCalc.DBPR.value = "";
document.buildingCalc.DCA.value = "";
document.buildingCalc.totalFee.value = "";
document.buildingCalc.Subtotaldisc.value = "";
document.buildingCalc.DBPRdisc.value = "";
document.buildingCalc.DCAdisc.value = "";
document.buildingCalc.totalFeedisc.value = "";
}
function calcBuilding(ICCVal) {
var validNums = "0123456789."
var flag = "yes";
var tempChar;
for (var c = 0; c < ICCVal.value.length; c++) {
tempChar = "" + ICCVal.value.substring(c, c + 1);
if (validNums.indexOf(tempChar) == "-1") {
flag = "no";
}
}
if (flag == "no") {
alert("Please enter only whole numbers.");
}
if (flag == "yes") {
if (document.buildingCalc.ICCVal.value == "") {
alert("Please enter the ICC valuation.");
} else {
var addlFee = 5.26;
var baseFee = 968.50;
var roundICC = Math.round(ICCVal.value - 100000) / 1000;
var permitFee = Math.round((addlFee * roundICC) * 1000) / 1000;
var permitFee2 = Math.round((permitFee + baseFee) * 100) / 100;
var planReviewRate = 0.25;
var planReview = Math.round((planReviewRate * permitFee2) * 1000) / 1000;
var Subtotal = Math.round((permitFee2 + planReview) * 100) / 100;
var DBPRRate = 0.015;
var DBPR = Math.round((DBPRRate * Subtotal) * 100) / 100;
var DCARate = 0.01;
var DCA = Math.round((DCARate * Subtotal) * 100) / 100;
var totalFee = Math.round((Subtotal + DBPR + DCA) * 100) / 100;
var Subtotaldisc = Math.round((Subtotal * 0.25) * 100) / 100;
var DBPRRatedisc = 0.015;
var DBPRdisc = Math.round((DBPRRatedisc * Subtotaldisc) * 100) / 100;
var DCARatedisc = 0.01;
var DCAdisc = Math.round((DCARatedisc * Subtotaldisc) * 100) / 100;
var totalFeedisc = Math.round((Subtotaldisc + DBPRdisc + DCAdisc) * 100) / 100;
document.buildingCalc.permitFee.value = permitFee2.toLocaleString();
document.buildingCalc.planReview.value = planReview.toLocaleString();
document.buildingCalc.Subtotal.value = Subtotal.toLocaleString();
document.buildingCalc.DBPR.value = DBPR.toLocaleString();
document.buildingCalc.DCA.value = DCA.toLocaleString();
document.buildingCalc.totalFee.value = totalFee.toLocaleString();
document.buildingCalc.Subtotaldisc.value = Subtotaldisc.toLocaleString();
document.buildingCalc.DBPRdisc.value = DBPRdisc.toLocaleString();
document.buildingCalc.DCAdisc.value = DCAdisc.toLocaleString();
document.buildingCalc.totalFeedisc.value = totalFeedisc.toLocaleString();
}
}
}
<form name="buildingCalc">
<table border="0" cellpadding="5" bordercolor="#000000">
<tr>
<td width="350" valign="bottom"><span class="body">Enter the ICC valuation (round to the nearest thousand) Example: 342778.20 is 343000 </span></td>
<td align="center" valign="bottom">
<div class="body">$<input type="text" name="ICCVal">
</div>
</td>
</tr>
<tr>
<td colspan="2">
<span class="body"><input type="button" onClick="calcBuilding(ICCVal)" value="Calculate building permit fee"></span>
</td>
</tr>
<tr>
<td>
<div class="body">Building permit fee</div>
</td>
<td align="center" class="body">$<input type="text" name="permitFee">
</td>
</tr>
<tr>
<td>
<div class="body">Plan review 25%</div>
</td>
<td class="body">$<input type="text" name="planReview">
</td>
</tr>
<tr>
<td>
<div class="body">Subtotal</div>
</td>
<td class="body">$<input type="text" name="Subtotal">
</td>
</tr>
<tr>
<td>
<div class="body">DBPR 1.5%</div>
</td>
<td class="body">$<input type="text" name="DBPR">
</td>
</tr>
<tr>
<td>
<div class="body">DCA 1%</div>
</td>
<td class="body">$<input type="text" name="DCA">
</td>
</tr>
<tr>
<td>
<div class="body">Total building permit fee</div>
</td>
<td class="body">$<input type="text" name="totalFee">
</td>
</tr>
<tr>
<td colspan="2" class="body">
<p><strong> <label>Does not include impact fees (if applicable), or Fire fees for commercial permits.</label>
</strong></p>
<p class="subpagesubheader">Fees effective Jan. 1, 2021 with 75% reduction applied </p>
</td>
</tr>
<tr>
<td>
<div class="body">Subtotal</div>
</td>
<td class="body">$<input type="text" name="Subtotaldisc">
</td>
</tr>
<tr>
<td>
<div class="body">DBPR 1.5%</div>
</td>
<td class="body">$<input type="text" name="DBPRdisc">
</td>
</tr>
<tr>
<td>
<div class="body">DCA 1%</div>
</td>
<td class="body">$<input type="text" name="DCAdisc">
</td>
</tr>
<tr>
<td>
<div class="body">Total building permit fee</div>
</td>
<td class="body">$<input type="text" `enter code here` name="totalFeedisc">
</td>
</tr>
<tr>
<td colspan="2" class="body">
<p><input type="button" onClick="clear_buildingCalc()" value="Clear calculation"></p>
</table>
</form>
For creating table I am using ng-repeat to render table row. onchange of dropdown I am calling a function which will run have some conditions on basis of that condition I am pushing an object to an array which bind to a scope variable.
My HTML
<div class="col-md-12" >
<div class="portlet light" >
<div class="portlet-title">
<div class="caption">Installment Structure </div>
</div>
<div class="portlet-body">
<div class="row">
<div class="col-md-12">
<table id="instalmentStructure" class="table table-bordered">
<tbody>
<tr>
<th style="width:20%;">From Installment
</th>
<th style="width:20%;">To Installment</th>
<th style="width:20%;">Installment Amount
</th>
<th style="width:20%;"></th>
</tr>
<tr class="quoteVal" ng-repeat="item in installmentLists">
<td >{{item.fromInst}}</td>
<td >{{item.toInst}}</td>
<td contenteditable="true" class="quoteVal">{{item.amtInst}}</td>
<td>
<span class="col-md-6">
<center>
<a ng-click="editRecord(item,$index)">
<i class="fa fa-pencil"></i>
</a>
</center>
</span>
<span class="col-md-6">
<center>
<a ng-click="deleteRecord(item,$index)">
<i class="fa fa-trash"></i>
</a>
</center>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
My Controller Code:
$scope.createInstallments = function () {
console.log("will create installments");
if (trialCalculCntrl.emiCalQde.bulletFreq == 12) {
console.log("Bullet Frequency is Yearly");
var bulletFreq = trialCalculCntrl.emiCalQde.bulletFreq;
$scope.installmentLists = [];
$scope.installmentObj = {
'fromInst': "",
'toInst': "",
'amtInst': ""
};
var remainder = tenure % 12;
if (remainder == 0) {
var numofrows = ((tenure * 2) / 12).toFixed(0);
for (var i = 1; i <= numofrows; i++) {
if (i == 1) {
$scope.installmentObj = {
'fromInst': i,
'toInst': bulletFreq - 1,
'amtInst': ""
};
$scope.installmentLists.push($scope.installmentObj);
} else if (i % 2 == 0) {
console.log("EVEN i: ", i);
var preval = $('tr.quoteVal').eq(i - 2).find('td:eq(1)').text();
console.log("Previous Val ", preval);
} else {
console.log("ODD i: ", i);
// var preval = $('tr.quoteVal').eq(i-1).find('td:eq(2)').text();
// console.log("Previous Val ",preval);
}
}
console.log("Instalment list : ", $scope.installmentLists);
} else {
var numofrows = (((tenure * 2) / 12) + 1).toFixed(0);
for (var i = 0; i < numofrows; i++) {
$scope.installmentObj = {
'fromInst': "",
'toInst': "",
'amtInst': ""
};
$scope.installmentLists.push($scope.installmentObj);
}
console.log("Instalment list : ", $scope.installmentLists);
}
}
};
Inside for loop after first run I am pushing the object to $scope.installmentLists array but it is not showing in to HTML so I am not able to read the array object in second run of for loop. I am using jQuery selectors to read, so console.log("Previous Val ", preval); gives me an empty string.
Place your jquery about console.log in setTimeout when changed $scope. Otherwise the jquery selector does not return expected because the HTML has not been rendered.
setTimeout(function() {
onsole.log("EVEN i: ", i);
var preval = $('tr.quoteVal').eq(i - 2).find('td:eq(1)').text();
console.log("Previous Val ", preval);
}, 500);
I have a table that consists of tr and tds and I show the percentage of sold ticket in third td.My code has problem whenever I write "Mytext" instead of number and calculate wrong moreover it shows infinity instead of showing write percentage. How can I define if the td has "mytext" instead of number , set the closest number in sold column instead of that text and then calculate ?For instance if there was Mytext in avalable column and 5 in sold column replace 5 with text and then calculate ?
here is my code :
$('table tbody tr').each(function() {
var $this = this,
td2Value = $('td:nth-child(2)', $this).text().trim().split(/\D+/);
$('span.result', $this).each(function (index, element) {
let v = $('td:nth-child(1)', $this).text().trim().split(/\D+/);
$(element).html(Math.round((td2Value[index] * 100 / v[index]) || 0) + '%');
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<table border="1">
<thead>
<tr>
<th> avalable</th>
<th> sold</th>
<th> result </th>
</tr>
</thead>
<tbody>
<tr>
<td>
Mytext<br>
10<br/>
</td>
<td>
5<br/>
2<br/>
</td>
<td>
<span class="result"></span><br/>
<span class="result"></span><br/>
</td>
</tr>
</tbody>
</table>
remove the regex number in available column, then check for the value if it equals Mytext change it
$('table tbody tr').each(function() {
var $this = this,
td2Value = $('td:nth-child(2)', $this).text().trim().split(/\D+/);
$('span.result', $this).each(function (index, element) {
//change starts here
let v = $('td:nth-child(1)', $this).text().trim().split(/\r?\n/);
if(v[index] != null && v[index].trim() == "Mytext")
{
v[index] = td2Value[index];
}
if(v[index] != null )
{
$(element).html(Math.round((td2Value[index] * 100 / v[index]) || 0) + '%');
}
//change ends here
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<table border="1">
<thead>
<tr>
<th> avalable</th>
<th> sold</th>
<th> result </th>
</tr>
</thead>
<tbody>
<tr>
<td>
Mytext<br>
10<br/>
</td>
<td>
5<br/>
2<br/>
</td>
<td>
<span class="result"></span><br/>
<span class="result"></span><br/>
</td>
</tr>
</tbody>
</table>
I have a list of results from year to year.
<tr *ngFor="let d of histroricalData; let i = index">
<td>
{{d.YearCalculation}}
</td>
<td>
{{d.OverallResult}}%
</td>
<td>
<!--subtract previous year from current -->
</td>
</tr>
How would I subtract the previus result from the current one in the loop?
This is what I want to achieve using a regular for loop
for (let i = 0; i < this.histroricalData.length; i++) {
if (this.histroricalData[i - 1] != null) {
let calc = this.histroricalData[i].OverallResult - this.histroricalData[i - 1].OverallResult
console.log(Math.round(calc * 100) / 100);
}
}
This a just a idea. You can initialize a array called indexArray something like [0,1,2,3,4,5,6] and use it for the loop as follows
<tr *ngFor="let d of histroricalData; let i of indexArray">
<td>
{{d.YearCalculation}}
</td>
<td>
{{d.OverallResult}}%
</td>
<td *ngIf="histroricalData[i - 1] != null">
{{histroricalData[i].OverallResult - histroricalData[i - 1].OverallResult}}
</td>
</tr>
You can always take help of index,
So, you can use
{{histroricalData[i - 1].OverallResult - histroricalData[i].OverallResult}}%
or,
{{(histroricalData[i - 1]['OverallResult']) - (histroricalData[i]['OverallResult'])}}%
The code becomes,
<tr *ngFor="let d of histroricalData; let i = index">
<td>
{{d.YearCalculation}}
</td>
<td>
{{histroricalData[i - 1].OverallResult - histroricalData[i].OverallResult}}%
</td>
<td>
<!--subtract previous year from current -->
</td>
</tr>
It works for you.
template:
<tr *ngFor="let d of histroricalData; let i = index">
<td>
{{d.YearCalculation}}
</td>
<td>
{{d.OverallResult}}%
</td>
<td *ngIf="i > 0">
{{getCalculatedValue(histroricalData[i].overallResult - histroricalData[i-1].overallResult)}}
</td>
</tr>
ts file:
getCalculatedValue(value){
return Math.round(value * 100) / 100;
}
I have the following table:
<table border="0" cellspacing="0" cellpadding="0" id="table1">
<tbody>
<tr>
<th onclick="sortTable(0, this); return false;" class="sort-up" order="-1">ColumnA</th>
<th style="width: 12em;" onclick="sortTable(1, this); return false;" class="sort-none">ColumnB</th>
<th style="width: 9em;" onclick="sortTable(2, this); return false;" class="sort-none">ColumnC</th>
<th style="width: 10em;" onclick="sortTable(3, this); return false;" class="sort-none">ColumnD</th>
<th style="width: 6em;">ColumnE</th>
</tr>
<tr id="tr217E9B6C" type="root" level="217E9B6C" depth="0">
<td class="evenListRow" id="nocenter">
<div class="tier1">Root A</div>
</td>
<td class="evenListRow">1</td>
<td class="evenListRow">2</td>
<td class="evenListRow">3</td>
<td class="evenListRow">4</a>
</td>
</tr>
<tr id="tr217E9B6C-6E781501" type="sub" level="217E9B6C-6E781501" depth="1">
<td class="oddListRow" id="nocenter">
<div class="tier2">Sub A</div>
</td>
<td class="oddListRow">5</td>
<td class="oddListRow">6</td>
<td class="oddListRow">7</td>
<td class="oddListRow">8</td>
</tr>
<tr id="tr217E9B6C-852AB6E5" type="sub" level="217E9B6C-852AB6E5" depth="1">
<td class="evenListRow" id="nocenter">
<div class="tier2">Sub B</div>
</td>
<td class="evenListRow">9</td>
<td class="evenListRow">10</td>
<td class="evenListRow">11</td>
<td class="evenListRow">12</td>
</tr>
<tr id="tr2BE7EAFE" type="root" level="2BE7EAFE" depth="0">
<td class="evenListRow" id="nocenter">
<div class="tier1">Root B</div>
</td>
<td class="evenListRow">13</td>
<td class="evenListRow">14</td>
<td class="evenListRow">15</td>
<td class="evenListRow">16</td>
</tr>
<tr id="tr2BE7EAFE-49A04568" type="sub" level="2BE7EAFE-49A04568" depth="1">
<td class="oddListRow" id="nocenter">
<div class="tier2">Sub C</div>
</td>
<td class="oddListRow">17</td>
<td class="oddListRow">18</td>
<td class="oddListRow">19</td>
<td class="oddListRow">20</td>
</tr>
<tr id="tr2BE7EAFE-DAE218A5" type="sub" level="2BE7EAFE-DAE218A5" depth="1">
<td class="evenListRow" id="nocenter">
<div class="tier2">Sub D</div>
</td>
<td class="evenListRow">21</td>
<td class="evenListRow">22</td>
<td class="evenListRow">23</td>
<td class="evenListRow">24</td>
</tr>
<tr id="tr4FFACE4A" type="root" level="4FFACE4A" depth="0">
<td class="oddListRow" id="nocenter">
<div class="tier1">Root C</div>
</td>
<td class="oddListRow">25</td>
<td class="oddListRow">26</td>
<td class="oddListRow">27</td>
<td class="oddListRow">28</td>
</tr>
<tr id="tr4FFACE4A-B9A443CA" type="sub" level="4FFACE4A-B9A443CA" depth="1">
<td class="evenListRow" id="nocenter">
<div class="tier2">Sub E</div>
</td>
<td class="evenListRow">29</td>
<td class="evenListRow">30</td>
<td class="evenListRow">31</td>
<td class="evenListRow">32</td>
</tr>
</tbody>
</table>
And I want to sort it, first by the "root" then by the "sub" items, which would mean that Root A will always have its Sub A, Sub B under it (sorted as well, but under it)
I used the following code which works on only on the "sub items", I cannot get it to work by doing a "mix", i.e. top and sub (separately sorted)
function sortTable(column, thisrow) {
var order = thisrow.getAttribute('order');
if (!order) {
order = 1;
}
var tbl = document.getElementById("table1").tBodies[0];
if (!tbl) {
return;
}
if (previousSortColumn && previousSortColumn.innerHTML != thisrow.innerHTML) {
previousSortColumn.setAttribute('class', 'sort-none');
}
previousSortColumn = thisrow;
var store = [];
/* Build a store object that has every element in the table, we will use this to sort */
for(var rowpos=1, len=tbl.rows.length; rowpos<len; rowpos++) { // skip row #1 as it is the header
var row = tbl.rows[rowpos];
var i_textContent = row.cells[column].textContent;
while(i_textContent.indexOf(' ') != -1) { // remove spaces
i_textContent = i_textContent.replace(' ', '');
}
var sortnr = i_textContent;
var type = row.getAttribute('type');
var level = row.getAttribute('level');
var depth = row.getAttribute('depth');
store.push({sortnr: sortnr, row:row, storelength:store.length, type:type, level:level, depth:depth});
}
/* We sort first roots then the elements under it */
store.sort(function(x,y) {
var xtype = x['type'];
var ytype = y['type'];
var result;
if (xtype == 'root' && ytype == 'root')
{
result = x['sortnr'].localeCompare(y['sortnr']);
} else {
return 0;
}
if (order == 1) {
return result;
} else {
return -1 * result;
}
});
/* We sort the elements under it */
store.sort(function(x,y) {
var xtype = x['type'];
var ytype = y['type'];
var xlevel = x['level'];
var ylevel = y['level'];
if (xlevel.lastIndexOf('-') > 0) {
xlevel = xlevel.substring(0, xlevel.lastIndexOf('-'));
}
if (ylevel.lastIndexOf('-') > 0) {
ylevel = ylevel.substring(0, ylevel.lastIndexOf('-'));
}
if (xlevel != ylevel || xtype == 'root' || ytype == 'root')
{
return x['storelength'] - y['storelength']; // return order inside array
}
var result = x['sortnr'].localeCompare(y['sortnr']);
if (order == 1) {
return result;
} else {
return -1 * result;
}
});
for(var i=0; i < store.length; i++) {
tbl.appendChild(store[i]['row']);
}
store = null;
}
Update 1:
Clicking once on 'ColumnB' would not affect the table (a bit of a bad example on my part), as the information is already sorted in the correct order, however another click should sort everything in reverse order
So both the Roots would be in reverse other, Root C, Root B, Root A, as well their sub items, Sub D before Sub C, ...
<table border="0" cellspacing="0" cellpadding="0" id="table1">
<tbody>
<tr>
<th onclick="sortTable(0, this); return false;" class="sort-up" order="-1">ColumnA</th>
<th style="width: 12em;" onclick="sortTable(1, this); return false;" class="sort-none">ColumnB</th>
<th style="width: 9em;" onclick="sortTable(2, this); return false;" class="sort-none">ColumnC</th>
<th style="width: 10em;" onclick="sortTable(3, this); return false;" class="sort-none">ColumnD</th>
<th style="width: 6em;">ColumnE</th>
</tr>
<tr id="tr4FFACE4A" type="root" level="4FFACE4A" depth="0">
<td class="oddListRow" id="nocenter">
<div class="tier1">Root C</div>
</td>
<td class="oddListRow">25</td>
<td class="oddListRow">26</td>
<td class="oddListRow">27</td>
<td class="oddListRow">28</td>
</tr>
<tr id="tr4FFACE4A-B9A443CA" type="sub" level="4FFACE4A-B9A443CA" depth="1">
<td class="evenListRow" id="nocenter">
<div class="tier2">Sub E</div>
</td>
<td class="evenListRow">29</td>
<td class="evenListRow">30</td>
<td class="evenListRow">31</td>
<td class="evenListRow">32</td>
</tr>
<tr id="tr2BE7EAFE" type="root" level="2BE7EAFE" depth="0">
<td class="evenListRow" id="nocenter">
<div class="tier1">Root B</div>
</td>
<td class="evenListRow">13</td>
<td class="evenListRow">14</td>
<td class="evenListRow">15</td>
<td class="evenListRow">16</td>
</tr>
<tr id="tr2BE7EAFE-DAE218A5" type="sub" level="2BE7EAFE-DAE218A5" depth="1">
<td class="evenListRow" id="nocenter">
<div class="tier2">Sub D</div>
</td>
<td class="evenListRow">21</td>
<td class="evenListRow">22</td>
<td class="evenListRow">23</td>
<td class="evenListRow">24</td>
</tr>
<tr id="tr2BE7EAFE-49A04568" type="sub" level="2BE7EAFE-49A04568" depth="1">
<td class="oddListRow" id="nocenter">
<div class="tier2">Sub C</div>
</td>
<td class="oddListRow">17</td>
<td class="oddListRow">18</td>
<td class="oddListRow">19</td>
<td class="oddListRow">20</td>
</tr>
<tr id="tr217E9B6C" type="root" level="217E9B6C" depth="0">
<td class="evenListRow" id="nocenter">
<div class="tier1">Root A</div>
</td>
<td class="evenListRow">1</td>
<td class="evenListRow">2</td>
<td class="evenListRow">3</td>
<td class="evenListRow">4</a>
</td>
</tr>
<tr id="tr217E9B6C-852AB6E5" type="sub" level="217E9B6C-852AB6E5" depth="1">
<td class="evenListRow" id="nocenter">
<div class="tier2">Sub B</div>
</td>
<td class="evenListRow">9</td>
<td class="evenListRow">10</td>
<td class="evenListRow">11</td>
<td class="evenListRow">12</td>
</tr>
<tr id="tr217E9B6C-6E781501" type="sub" level="217E9B6C-6E781501" depth="1">
<td class="oddListRow" id="nocenter">
<div class="tier2">Sub A</div>
</td>
<td class="oddListRow">5</td>
<td class="oddListRow">6</td>
<td class="oddListRow">7</td>
<td class="oddListRow">8</td>
</tr>
</tbody>
</table>
I solved your problem. I did reorganize the code to make it a lot more readable. Most of the logic is what you provided, i just added small bits. And btw, you have duplicate id references on id="nocenter" in your html.
Here is a working jsfiddle of my solution. The HTML is exactly the one you provided , with errors and all and no listener on column E. This js fiddle version has some more subs on root A. You can play with it as you will (add extra data). Summary of the code comes after it in the answer.
Update - taken your new input data in the comments , i updated the jsfiddle it would seem i kept the root in place, and it jumped out of subset. It was a matter of changing 1 line in sorting the subs. The new fiddle.
var ASC = 1;
var DESC = -1;
var SORTNR_INDEX = 0;
var LOWER = 1;
var UPPER = 2;
var previousSortColumn ;
var order;
/* The original build store you provided */
var buildStore = function(column,tbl){
var store = [];
for (var rowpos = 1, len = tbl.rows.length; rowpos < len; rowpos++) { // skip row #1 as it is the header
var row = tbl.rows[rowpos];
var i_textContent = row.cells[column].textContent;
while (i_textContent.indexOf(' ') != -1) { // remove spaces
i_textContent = i_textContent.replace(' ', '');
}
var sortnr = i_textContent;
var type = row.getAttribute('type');
var level = row.getAttribute('level');
var depth = row.getAttribute('depth');
store.push({sortnr: sortnr, row: row, storelength: store.length, type: type, level: level, depth: depth});
}
return store;
}
// the order convention you offered
var triggerOrder = function(){
if (order==ASC){
order = DESC;
} else if (order==DESC || !order){
order = ASC;
}
}
// the code you provided
var getLevel = function(obj){
if (obj && obj.lastIndexOf('-') > 0) {
return obj.substring(0, obj.lastIndexOf('-'));
}
return obj;
}
function sortRoot(a,b){
var aSort = a[SORTNR_INDEX], bSort = b[SORTNR_INDEX];
return compareWithOrder(aSort,bSort);
};
var sortSubs = function(x,y){
var xtype = x['type'];
var ytype = y['type'];
if (xtype == 'root'){
return -1;
} else if (xtype == ytype) {
var xSort = x['sortnr'];
var ySort = y['sortnr'];
return compareWithOrder(xSort,ySort);
}
}
var compareWithOrder = function(x,y){
if (isNaN(parseInt(x))) {
return order * x.localeCompare(y);
} else {
x = parseInt(x);
y = parseInt(y);
if (x < y) {
return -1 * order;
} else if (x > y) {
return 1 * order;
} else {
return 0;
}
}
};
//assumes they are aligned by depth (i.e. will always have a root then subs). if not, an additional sort can be made beforehand
function getGroupsByLevel(store){
var group = [];
var groupIndex=0;
var lower =0, upper, sortNo;
if (store.length > 0) {
var x,y;
for (var i = 0; i < store.length; i++) {
x = store[i];
if (store[i+1]){
y = store[i+1]
} else{
y = {};
}
var xtype = x['type'];
var ytype = y['type'];
if (xtype=='root'){
sortNo = x['sortnr'];
}
var xlevel = getLevel(x['level']);
var ylevel = getLevel(y['level']);
if (xlevel != ylevel){
group[groupIndex] = [sortNo,lower,i];
lower=i+1;
groupIndex++;
}
}
}
return group;
};
function sortTable(column, thisrow) {
order = thisrow.getAttribute('order');
triggerOrder();
thisrow.setAttribute('order',order);
var tbl = document.getElementById("table1").tBodies[0];
if (!tbl) return;
/* Build a store object that has every element in the table, we will use this to sort */
var store = buildStore(column,tbl);
var groups = getGroupsByLevel(store);
groups.sort(sortRoot);
var newStore=[];
for (var i=0;i<groups.length;i++){
var group = groups[i];
var rootAndSubs = store.slice(group[LOWER],group[UPPER]+1);
rootAndSubs.sort(sortSubs);
newStore=newStore.concat(rootAndSubs);
}
//update table
for (var i = 0; i < newStore.length; i++) {
tbl.appendChild(newStore[i]['row']);
}
store = null;
order = null;
}
Basically it goes like this:
set +trigger the order
build the store just like you intended
get groups of roots and their subs
sort the groups and then sort the each of the subs for the group
update the view
This is the first approach i thought of.