Trouble with JavaScript arithmetic and NaN values from the DOM - javascript

The problem I'm having is with my function calculate(). The purpose of the program is creating a dynamic spreadsheet with JavaScript and html, with a CSS for decoration purposes.
So far I have it insert the values into the spreadsheet, however when attempting to run a simple addition calculation on it the values that I'm grabbing from the cells are not properly being grabbed.
Here's the function in specific that's giving me the problem. My apologies for the sloppy code. Any help would be appreciated, I'm sure it's something simple that I'm missing.
EDIT:: Corrected the issue, for those of you wondering, I had to call not innerHTML.text, or value. It had to be simply the .innerHTML on the cell in question.
function calculate() {
//create two variables to get the values in the row and columns
var row1, row2, col1, col2, total, totalInsert;
var row = document.getElementById("row");
var col = document.getElementById("col");
var value = document.getElementById(row + "_" + col);
var formula = document.getElementById("inputText");
formula = formula.value; //=SUM(1,2,2,1)
formula1 = formula;
//get the raw values to ints
row = row.value;
col = col.value;
//get JUST the formula in questions part
formula = formula.substr(0,4);
//Parsing the the selected cells from =sum(1,1,2,1) into Cell ID's
col1 = parseInt(formula1.substr(5, 1));//row 1
row1 = parseInt(formula1.substr(7, 1));
col2 = parseInt(formula1.substr(9, 1));
row2 = parseInt(formula1.substr(11, 1)); // column2
//this gives us the proper cell's address. id=1_1
var td1 = col1 + "_" + row1;
var td2 = col2 + "_" + row2;
//problem starts around here
var sum = document.getElementById(td1);
var sum2 = document.getElementById(td2);
//this returns a undefined value
sum = sum.value;
sum2 = sum2.value;
//this restults in a NaN
sum = parseInt(sum);
sum2 = parseInt(sum2);
//creating the total value
total = sum + sum2;
//returning values
totalInsert = document.getElementById(td1).innerHTML = total;
}

This part seems odd to me, you are selecting two elements and then combining them as a string to find another element?
var row = document.getElementById("row");
var col = document.getElementById("col");
var value = document.getElementById(row + "_" + col);
I'm not sure that would do what you are expecting... In my experience you'd get something like this:
document.getElementById('[object Object]_[object Object]');
or:
document.getElementById('[object HTMLDivElement]_[object HTMLDivElement]');
... depending on the browser. Now obviously you could have an element with that kind of id, but that would be a little bit bonkers ;)
It looks to me like the row and col elements are inputs, so maybe you actually mean:
var row = document.getElementById("row");
var col = document.getElementById("col");
var value = document.getElementById(row.value + "_" + col.value);

Related

How do I convert string cell value of a number into a number increment by 1?

I am trying to fetch the last row's row id (located in the first column) to increment it by one once there is a new row added. However I am not sure on how to convert the string into a number to increment by 1
What I tried:
var url = "";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
var rowID = ws.getRange(ws.getLastRow(), 1).getValues().toString().parseInt()+1;
console.log(rowID);
Which is not a function based on the log.
If the values of your id is 1, 2, 3,,, , how about the following modification?
From:
var rowID = ws.getRange(ws.getLastRow(), 1).getValues().toString().parseInt()+1;
To:
var rowID = (parseInt(ws.getRange(ws.getLastRow(), 1).getValue(), 10) || 0) + 1;
or
var rowID = (Number(ws.getRange(ws.getLastRow(), 1).getValue()) || 0) + 1;
or, in the event the value in the last row is known to always be a number or a blank, the following modification might be able to be used.
var rowID = ws.getRange(ws.getLastRow(), 1).getValue() + 1;
Reference:
parseInt()

vlookup for different columns simultaneously filling the whole sheet

Below is a code that I found here : https://webapps.stackexchange.com/questions/123670/is-there-a-way-to-emulate-vlookup-in-google-script
I tried to optimize it to my use case in which
to vlookup from source sheet 'data', and fill in values in destination sheet 's'. The problem is that this code does this only for one row. Is there a way to loop over all rows and vlookup and fill in efficiently?
/* recall that we want the follwoing columns => E, F, G, H, M
/*/
function khalookup(){
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = SpreadsheetApp.openById("mysheetid");
var searchValue = s.getRange("B2:B").getValues();
var dataValues = data.getRange("A3:A").getValues();
var dataList = dataValues.join("ღ").split("ღ");
var index = dataList.indexOf([searchValue]);
var newRange = []
var row = index + 3;
var foundValue = data.getRange("E"+row).getValue();
var foundValue1 = data.getRange("F"+row).getValue();
var foundValue2 = data.getRange("G"+row).getValue();
var foundValue3 = data.getRange("H"+row).getValue();
var foundValue4 = data.getRange("M"+row).getValue();
s.getRange("K2").setValue(foundValue);
s.getRange("L2").setValue(foundValue1);
s.getRange("M2").setValue(foundValue2);
s.getRange("N2").setValue(foundValue3);
s.getRange("O2").setValue(foundValue4);
}
here is the source sheet where the vlookup shall happen based on the ID "Column A"
And here is how the destination sheet shall look like after the vlookup based on ID "Column B" have been made.
You can do iterate with a loop, e.g. a for loop
Assuming you would like to loop through all rows from index + 3 to the last row, you can modify your code as following:
...
var row = index + 3;
var lastRow = s.getLastRow();
for (var i = row; i <= lastRow; i++){
var foundValue = data.getRange("E"+i).getValue();
var foundValue1 = data.getRange("F"+i).getValue();
var foundValue2 = data.getRange("G"+i).getValue();
var foundValue3 = data.getRange("H"+i).getValue();
var foundValue4 = data.getRange("M"+i).getValue();
s.getRange("K" + (2+i-row)).setValue(foundValue);
s.getRange("L" + (2+i-row)).setValue(foundValue1);
s.getRange("M" + (2+i-row)).setValue(foundValue2);
s.getRange("N" + (2+i-row)).setValue(foundValue3);
s.getRange("O" + (2+i-row)).setValue(foundValue4);
}
Note that later on you might want to progress from using getValue() and setValue() to getValues() and setValues() - that will make your code execution faster.

Optimizing Code - getValue() and looping

I'm a bit of newbie at coding, especially Javascript/Google-script language. I've created the code below, and it works, but now that I've got a working code I'd like to see how I can optimize it. It seems to me that all of the getValue() calls are a major performance hit, and I've never really been good at optimizing loops. Anyone know a better way to accomplish the same as this code?
What it does: Checks each spreadsheet in one of my folders to see if it needs to have the rest of the script run. If true, it opens that sheet and counts the number of rows that have data, using that to limit the amount of rows it checks in the loop. It then looks for any row marked for push and copies that range to another spreadsheet in my drive. It then continues to the next file in the folder and does the same.
Here's my code:
function myVupdate() {
try {
var folder = DriveApp.getFolderById("123abc"),
files = folder.getFiles();
while (files.hasNext()) {
var file = files.next(),
sss = SpreadsheetApp.open(file);
SpreadsheetApp.setActiveSpreadsheet(sss);
//Work orders update
var ss = sss.getSheetByName("Sheet2"),
refresh = ss.getRange("W3").getValue();
if (refresh == 0) {continue};
var avals = ss.getRange("D5:D").getValues(),
count = avals.filter(String).length,
rows = count + 5
var val = ss.getDataRange().getValues();
for (var row=5; row < rows; row++) {
var cell = ss.getDataRange().getCell(row, 23).getValue();
if (cell == 0) {
var cells = [["v" + "WO-" + val[row-1][3] + "_" + val[row-1][2],val[row-1][13],val[row-1][14],val[row-1][15],new Date()]];
var tss = SpreadsheetApp.openById("target_spreadsheet"),
ts = tss.getSheetByName("Sheet5");
ts.insertRowBefore(2);
var last_hmy = ts.getRange(3,1).getValue();
ts.getRange(2,1).setValue(last_hmy+1);
ts.getRange(2,2,cells.length,cells[0].length).setValues(cells);
ts.getRange(2,7).setValue(sss.getName());
ss.getRange(row,17).setValue(last_hmy+1);
ss.getRange(row,18,cells.length,cells[0].length).setValues(cells);
//Turnover update
var ss = sss.getSheetByName("Sheet1"),
avals = ss.getRange("D5:D").getValues(),
count = avals.filter(String).length,
rows = count + 5
var val = ss.getDataRange().getValues();
}
}
for (var row=5; row < rows; row++) {
var cell = ss.getDataRange().getCell(row, 24).getValue();
if (cell == 0) {
var cells = [["v" + val[row-1][3] + "_" + val[row-1][2],val[row-1][12],val[row-1][15],val[row-1][16],new Date()]];
var tss = SpreadsheetApp.openById("target_spreadsheet"),
ts = tss.getSheetByName("Sheet5");
ts.insertRowBefore(2);
var last_hmy = ts.getRange(3,1).getValue();
ts.getRange(2,1).setValue(last_hmy+1);
ts.getRange(2,2,cells.length,cells[0].length).setValues(cells);
ts.getRange(2,7).setValue(sss.getName());
ss.getRange(row,18).setValue(last_hmy+1);
ss.getRange(row,19,cells.length,cells[0].length).setValues(cells);
}
}
}
}
catch(e) {
// Browser.msgBox("An error occured. A log has been sent for review.");
var errorSheet = SpreadsheetApp.openById ("target_sheet").getSheetByName("Error Log"),
source = sss.getName();
lastRow = errorSheet.getLastRow();
var cell = errorSheet.getRange('A1');
cell.offset(lastRow, 0).setValue(e.message);
cell.offset(lastRow, 1).setValue(e.fileName);
cell.offset(lastRow, 2).setValue(e.lineNumber);
cell.offset(lastRow, 3).setValue(source);
cell.offset(lastRow, 4).setValue(new Date());
MailApp.sendEmail("my#email.com", "Error report - " + new Date(),
"\r\nSource: " + source + "\r\n"
+ "\r\nMessage: " + e.message
+ "\r\nFile: " + e.fileName
+ "\r\nLine: " + e.lineNumber
);
}
}
Hello and welcome to Stack Overflow,
first of all, you are correct. The more getValue(), or setValue() calls you do the worse the performance, read more on best practices here. Google recommends you batch these as much as possible. One thing that immediately springs to attention is the following:
var val = ss.getDataRange().getValues();
so now you have all the values on the sheet in a 2D array. That means that in the following bit
var ss = sss.getSheetByName("Sheet2"),
refresh = ss.getRange("W3").getValue();
if (refresh == 0) {continue};
var avals = ss.getRange("D5:D").getValues(),
count = avals.filter(String).length,
rows = count + 5
var val = ss.getDataRange().getValues();
for (var row=5; row < rows; row++) {
var cell = ss.getDataRange().getCell(row, 23).getValue();
every single getValue() or getValues() is no longer necessary. Instead, you know that refresh = val[2][22] because you need the 3rd row and 23rd column, as you already have the entire range that has data from that sheet.
Same with avals as all values in range D5:D are in vals[n][3], where n starts from 4. Remember, the array index starts from 0 (so first row and first column is vals[0][0].
So anywhere you are trying to use getValues() from the ss spreadsheet, you already have that data. What you can also do, is manipulate the array you have, so you always change the values only in that array. Once you are done with it, you use ss.getDataRange().setValues(vals) to push the entire array back to the same range (you can just store the range in a variable like datRange = ss.getDataRange() and then do datRange.setValues(vals).
You will just need to work with a separate data array for any other sheet. I did not go into detail for the rest of the code as the same ideas go throughout. Since you already grab everything with getValues() there is no longer any reason to use getValue() for any cell within that range.

Does .replace not work in a for loop?

So I'm trying to just make a ".replace" loop, but something mysterious is happening.
var cell = "r1c1";
for (i = 0; i <= 4; i++){
cell = cell.replace(cell[3],i+1);
My expected output:
cell = "r1c1"
cell = "r1c2"
cell = "r1c3"
cell = "r1c4"
cell = "r1c5"
The actual output:
cell = "r1c2"
cell = "r2c1"
cell = "r2c3"
cell = "r2c4"
cell = "r2c5"
As you can see, it runs normal EXCEPT for the second iteration.
What in the world am I doing so wrong?
cell.replace(cell[3], i+1) is going to replace the first instance of the '1' character it finds in the string 'r1c1'. In this case it is the '1' at position 1 that gets replaced instead of the '1' at position 3.
To get the results you want, try
var cell = "r1c1";
for (i = 0; i <= 4; i++){
cell = cell.substring(0, cell.length-1)+(i+1);
}
You can use a regular expression: /^r(\d+)c(\d+)/:
var row_col = 'r1c1';
var match = /^r(\d+)c(\d+)/.exec(row_col); // match string
var row = +match[1]; // extract row
var col = +match[2]; // extract column
// edit row and col as needed
row_col = 'r' + row + 'c' + col; // "reassemble" string
This will take care of bigger row/column numbers than 9. If that is not to be expected, then read about String.prototype.substring():
var row_col = 'r1c1';
var row = +row_col.substring(1,2);
var col = +row_col.substring(3,4)
I don't like this, but the fix I ended up using looks like this:
cell = "r"+(j)+cell.substr(2,2);
Since I'm inevitably answering my own question, here, I still want to ask for comments:
How elegant would you say a solution like this is in the long run?

Use variable with HTML string and count number of <tr>s

I have a variable that stores some HTML:
var rows = '<tr><td></td></tr>.....';
and I want to use jQuery to count the number of <tr> tags there are. I can do it when it's not in a variable:
$('#some-table tr').length
but can't using the variable. I have tried something like this:
$(rows)
but not sure what to do with it to get the number of <tr>s out.
jQuery-ify the rows, then query:
var rows = '<tr><td></td></tr>.....';
var numRows = $(rows).filter('tr').length;
or...
var rows = '<tr><td></td></tr>.....';
var numRows = $('<tbody>' + rows + '</tbody>').find('tr').length;
// or
var numRows = $('<tbody>' + rows + '</tbody>').children().length;
var count = rows.match(/<tr>/g);
alert(count.length);

Categories