Calculate cell value based on formula and values in other cells - javascript

I have a table in a HTML file, and I want to calculate one cell's value based on other cells' values and a cell value as a formula. Take a look at this picture:
In my table, I have 1900 ratios and three factors in three columns. How I can calculate the result column values based on their respective formula column value?
Note: I use only *, /, +, and - in my formulas. Also, the table has an ID (ratiotable), but the <tr> and <td> don't have any ID or class names assigned to them.
If you know another webpage that did something like this, please let me see this.
Thank you very much.
Edit: The HTML code is like this. Note, I snipped a number of the rows. The actual number of rows is 1900:
<table>
<thead>
<tr>
<th>Ratio Name</th>
<th>Factor1</th>
<th>Factor2</th>
<th>Factor3</th>
<th>Formula</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<td>Name1</td>
<td>22</td>
<td>11</td>
<td></td>
<td>Factor1/Factor2</td>
<td></td>
</tr>
<tr>
<td>Name2</td>
<td>22</td>
<td>33</td>
<td>11</td>
<td>Factor1/Factor2*Factor3</td>
<td></td>
</tr>
<tr>
<td>Name1</td>
<td>12</td>
<td></td>
<td>4</td>
<td>(Factor1+Factor2)/Factor3</td>
<td></td>
</tr>
</tbody>

This was a really interesting challenge. I've put together a little JSFiddle here that does what you describe. It essentially replaces the string parts of the formula with their value counterparts, then evals the result. Hopefully this helps!
My Markup
<table>
<thead>
<tr>
<th>Ratio Name</th>
<th>Factor1</th>
<th>Factor2</th>
<th>Factor3</th>
<th>Formula</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<td>Name1</td>
<td>22</td>
<td>11</td>
<td></td>
<td>Factor1/Factor2</td>
<td></td>
</tr>
<tr>
<td>Name2</td>
<td>22</td>
<td>33</td>
<td>11</td>
<td>Factor1/Factor2*Factor3</td>
<td></td>
</tr>
<tr>
<td>Name1</td>
<td>12</td>
<td></td>
<td>4</td>
<td>(Factor1+Factor2)/Factor3</td>
<td></td>
</tr>
</tbody>
</table>
​
My JavaScript
$(function() {
$('tbody tr').each(function() {
var $this = $(this),
f1 = $this.find('td').eq(1).text() || 0,
f2 = $this.find('td').eq(2).text() || 0,
f3 = $this.find('td').eq(3).text() || 0,
formula = $this.find('td').eq(4).text(),
resultTd = $this.find('td').last();
formula = formula.replace('Factor1',f1)
.replace('Factor2',f2)
.replace('Factor3',f3);
resultTd.text(eval(formula));
});
});​

loop through all rows and use javascript eval() function
function cellValue(x,y){
return $('#ratiotable').find('tr:eq('+(y+1)+')>td:eq('+(x+1)+')').text();
}
function setCellValue(x,y,s){
$('#ratiotable').find('tr:eq('+(y+1)+')>td:eq('+(x+1)+')').text(s);
}
$(function(){
$.each($('#ratiotable tr'),function(i,tr){
var Factor1 = parseInt(cellValue(0,i));
var Factor2 = parseInt(cellValue(1,i));
var Factor3 = parseInt(cellValue(2,i));
var Formula = $.trim(cellValue(3,i));
var Result = eval(Formula);
setCellValue(4,i,Result);
});
});
like this

Related

Changing Table's tr:even with a different background color

I know how to do this with jquery, by doing this:
$("#rateTable tr:even").css("background", "#e7edea");
However, the format I am using will not allow jquery. I tried the code below, but it is saying that the style is null. Does anyone see what I am doing wrong?
document.getElementById("rateTable tr:even").style.background = "#e7edea";
<table id="rateTable">
<tr>
<td>Blue</td>
<td>Green</td>
</tr>
<tr>
<td>Red</td>
<td>Purple</td>
</tr>
<tr>
<td>Teal</td>
<td>Yellow</td>
</tr>
</table>
You don't really need JavaScript for this. It could been done in plain CSS with the nth-child pseudo-class.
tr:nth-child(even) {
background: #E7EDEA;
}
<table id="rateTable">
<tr>
<td>Blue</td>
<td>Green</td>
</tr>
<tr>
<td>Red</td>
<td>Purple</td>
</tr>
<tr>
<td>Teal</td>
<td>Yellow</td>
</tr>
</table>
You can do it with #rateTable tr:nth-of-type(even) as your selector. document.selectElementById() only works when you give it the exact string being used as an id, not a CSS selector. I would do it with CSS because it will update automatically and is likely faster, but you can do it in JS if you want:
const evenRows = document.querySelectorAll("#rateTable tr:nth-of-type(even)")
for (const row of evenRows) row.style.background = "#e7edea"
<table id="rateTable">
<tr>
<td>Blue</td>
<td>Green</td>
</tr>
<tr>
<td>Red</td>
<td>Purple</td>
</tr>
<tr>
<td>Teal</td>
<td>Yellow</td>
</tr>
<tr>
<td>Orange</td>
<td>Pink</td>
</tr>
</table>
Try to use querySelectorAll and next iterate over received collection to change background. See below:
var trToChange = document.querySelectorAll("#rateTable tr:nth-child(even)");
for(var i = 0; i < trToChange.length; i++){
trToChange[i].style.background = "#e7edea";
}

How to remove TR if there is a specific text inside its TD's in jquery?

I want to remove the TR if its 2nd TD value is similar to another TRs TD value and it's last TD value shouldn't be HIT. And the another scenario is if I have 3 TRs with the same data then 2 of them should be removed and 1 should remain there.
Example:
<table>
<tr>
<td>ID</td>
<td>Ref No</td>
<td>Name</td>
<td>Result</td>
</tr>
<tr>
<td>1</td>
<td>1121</td>
<td>Joseph</td>
<td>CLEAR</td>
</tr>
<tr>
<td>2</td>
<td>1122</td>
<td>Mike</td>
<td>CLEAR</td>
</tr>
<tr>
<td>3</td>
<td>1122</td>
<td>Mike</td>
<td>CLEAR</td>
</tr>
<tr>
<td>4</td>
<td>1122</td>
<td>Mike</td>
<td>HIT</td>
</tr>
<tr>
<td>5</td>
<td>1123</td>
<td>Jim</td>
<td>HIT</td>
</tr>
<tr>
<td>6</td>
<td>1124</td>
<td>James</td>
<td>CLEAR</td>
</tr>
<tr>
<td>7</td>
<td>1124</td>
<td>James</td>
<td>CLEAR</td>
</tr>
<tr>
<td>8</td>
<td>1124</td>
<td>James</td>
<td>CLEAR</td>
</tr>
</table>
What I want:
<table>
<tr>
<td>ID</td>
<td>Ref No</td>
<td>Name</td>
<td>Result</td>
</tr>
<tr>
<td>1</td>
<td>1121</td>
<td>Joseph</td>
<td>CLEAR</td>
</tr>
<tr>
<td>4</td>
<td>1122</td>
<td>Mike</td>
<td>HIT</td>
</tr>
<tr>
<td>5</td>
<td>1123</td>
<td>Jim</td>
<td>HIT</td>
</tr>
<tr>
<td>6</td>
<td>1124</td>
<td>James</td>
<td>CLEAR</td>
</tr>
</table>
Can anybody tell me how to achieve this task?
Any help would be highly appreciated.
So i made this clumsy answer for you. You can check it out in the fiddle here.
EDIT: after some discussion about what should the behaviour be, i updated the fiddle. so now it adds the check if there are any fields in the duplicates that have a "HIT" value in fourth column it will keep the first row with HIT value, otherwise it will keep the first value for each unique second column value.
I am sure there is a better/simpler/more effective way to do this with jQuery, but that is what I came up with. The basic algorithm is this: get all rows and iterate. For each row: find the value in second td (column), check all subsequent rows, fetch the value in second column there and compare them. if they are the same, remove the duplicate row from DOM.
//get the table rows, this should be done with a different selector if there are more tables e.g. with class or id...
$tableRows = $("tr");
//iterate over all elements (rows)
$tableRows.each(function(index, element) {
var $element = $(element);
//get the value of the current element
var currentRowValue = $element.find("td:nth-child(2)").text();
//check all elements that come after the current element if the value matches, if so, remove the matching element
for (var i = index + 1; i < $tableRows.length; i++) {
var $rowToCompare = $($tableRows[i]);
var valueToCompare = $rowToCompare.find("td:nth-child(2)").text();
if(valueToCompare === currentRowValue) {
//remove the duplicate from dom
//if the second row (the duplicate) has 4th column of "HIT" then keep the second row and remove the first row
var duplicateRowFourthColumnVal = $rowToCompare.find("td:nth-child(4)").text();
if(duplicateRowFourthColumnVal == "HIT") {
$element.remove();
}
else {
$rowToCompare.remove();
}
}
}
});`

Clone table and filter out rows

Hello I got a question regarding cloning a table.
I want to filter out some specific results and output those results.
Let me explain myself better with HTML code:
<table class="table table-striped" id="ProfileList2">
<tbody>
<tr>
<td>21</td>
<td>2014-02-28 21:12:12</td>
<td>20</td>
<td>one</td>
<td>166</td>
</tr>
<tr>
<td>22</td>
<td>2014-03-01 14:04:35</td>
<td>20</td>
<td>one</td>
<td>0</td>
</tr>
<tr>
<td>23</td>
<td>2014-03-03 15:22:56</td>
<td>20</td>
<td>one</td>
<td>21</td>
</tr>
<tr>
<td>24</td>
<td>2014-03-03 17:15:56</td>
<td>20</td>
<td>one</td>
<td>21</td>
</tr>
<tr>
<td>25</td>
<td>2014-03-03 17:50:49</td>
<td>20</td>
<td>one</td>
<td>0</td>
</tr>
<tr>
<td>26</td>
<td>2014-03-05 17:33:42</td>
<td>20</td>
<td>one</td>
<td>24</td>
</tr>
</tbody>
</table>
<p class="result"></p>
In my Javascript I want to filter out all the results that have within the last <td> or every <tr> the value 0.
var $mainTable = $("#ProfileList2");
var $training = $mainTable.clone("tr td:nth-child(5):not(:contains('0'))");
$('p').append($training);
This code will clone a direct copy without doing any filtering.
If I change the clone function to, for example: find function, it will only return the table-data: 166 21 21 24.
Is there any function available that will return the whole table-row instead of only the table-data?
JSFIDDLE OVER HERE
To grab the table rows, change this:
$mainTable.find("tr td:nth-child(5):not(:contains('0'))")
… to this:
$mainTable.find("tr td:nth-child(5):not(:contains('0'))").parent()
Also note that a paragraph element cannot contain a table element according to the specification. (Although most browsers allow it.) Changing it to a div would be better.
Fiddle 1
Also note that the contains selector will search for a 0 within the cell, so it will match cells containing "160" as well as those containing "0."
Instead, you could filter to exclude only cells that equal "0":
$mainTable
.find("tr td:nth-child(5)")
.filter(function() {return $(this).text() != '0';})
.parent();
Fiddle 2
Well, you could copy the whole thing and then filter out what you don't like, how about that?
var copy = $("#ProfileList2").clone();
$('p').append(copy);
copy.find("tr td:nth-child(5):contains('0')").parent().remove();
Make note that it is not a good idea to have multiple elements with the same id.

match words that appear at the beginning of the line in a table cell

Please take a look at this FIDDLE. How would you make sure it only matches the occurrence of Sodium that appear at the beginning of the line in a table cell, for example :
<td>Sodium</td>, <td>Sodium (from Kitchen Salt)</td>
but not
<td>Vitamin sodium</td>,<td>Fish Sodium</td>
My attempt
`var find_Sodium = /^Sodium/
alert($('.'+title+'table').find('td:contains(find_Sodium)').next().html());`
isn't working.
$.ajax({
url: "url.json",
success: function (data) {
$(data.query.results.json.json).each(function (index, item) {
var title = item.title;
var table = item.table;
if (table.indexOf("Sodium") >= 0) {
$('.'+ title+'table').html(''+table+'');
var find_Sodium = /^Sodium/;
alert($('.'+title+'table').find('td:contains(find_Sodium)').next().html());
}
});
},
error: function () {}
});
Table Structure:
<table class="tablesorter">
<thead>
<tr>
<td>Ingredient</td>
<td>Amount</td>
<td>% Daily Value**</td>
</tr>
</thead>
<tbody>
<tr>
<td>Calories</td>
<td>10</td>
<td></td>
</tr>
<tr>
<td>Sodium</td>
<td>2g</td>
<td><1</td>
</tr>
<tr>
<td>Vitamin C</td>
<td>110mg</td>
<td>4</td>
</tr>
<tr>
<td>Potassium sodium</td>
<td>235mg</td>
<td>6</td>
</tr>
<tr>
<td>Omega 6</td>
<td>1100mg</td>
<td>*</td>
</tr>
<tr>
<td>Vitamin Sodium</td>
<td>1200mg</td>
<td>*</td>
</tr>
<tr>
<td>Vitamin E</td>
<td>300mg</td>
<td>*</td>
</tr>
</tbody>
</table>
:contains does not accept a regex, the way to do this is to filter()
$('.'+title+'table').find('td').filter(function() {
return $(this).text().indexOf('Sodium') === 0;
}).next().html();
FIDDLE
using indexOf === 0 makes sure Sodium has an index of zero, being the first thing to occur in the elements text

Getting all data in table base on some highlighted row

This is the scenario. I have a <table> like this:
<table>
<tr>
<th>No.</th>
<th>Name</th>
<th>Price</th>
</tr>
<tr>
<td>1</td>
<td>Coca cola</td>
<td>5</td>
</tr>
<tr>
<td>2</td>
<td>Pepsi</td>
<td>5</td>
</tr>
<tr>
<td>3</td>
<td>Water</td>
<td>3</td>
</tr>
</table>
Now, user will highlight 2 first rows, for example. My task is to extract the appropriate 3 XPaths to get data in each column.
Does anyone have any idea about this? I have thought about it a lot but haven't found any good solution yet. Please help me. Thanks a lot.
EDIT!!!
You can try using Scraper from Google Chrome extension for more detail. What I want is just a little bit like it. Thanks.
Let's say you add a class to the highlighted rows, and after highlighting your html DOM is like this:
<table id="highlights">
<tr>
<th>No.</th>
<th>Name</th>
<th>Price</th>
</tr>
<tr class="highlighted">
<td>1</td>
<td>Coca cola</td>
<td>5</td>
</tr>
<tr class="highlighted">
<td>2</td>
<td>Pepsi</td>
<td>5</td>
</tr>
<tr>
<td>3</td>
<td>Water</td>
<td>3</td>
</tr>
</table>
I added id="highlights" just to make it distingushable, if you have other tables in the page. then this is what you have to do:
var noXpath = "//table[#id='highlights']/tbody/tr[#class='highlighted']/td[1]/text()";
var nameXpath = "//table[#id='highlights']/tbody/tr[#class='highlighted']/td[2]/text()";
var priceXpath = "//table[#id='highlights']/tbody/tr[#class='highlighted']/td[3]/text()";
function getData(xpathQuery) {
var iterator = document.evaluate(xpathQuery, document.body, null, XPathResult.ANY_TYPE, null);
var nodes = [];
var node;
while (node = iterator.iterateNext()) {
nodes.push(node.nodeValue);
}
return nodes;
}
var noData=getData(noXpath);
var namesData=getData(nameXpath);
var priceData=getData(priceXpath);
and this is the working DEMO.

Categories