Building up a variable with numbers in brackets for a loop - javascript

I am beginning work on creating my first for loop (for 29 times - 0 to 28) but the numbers have to be presented in square brackets.
I eventually want to build of a string of 0's and 1's (zeros and ones). The value of cellData will always be 0 (zero) or 1 (one).
I want to change the cellindex variable first number starting at zero and finishing at 28. e.g.
And later do the same thing with the second number (in another loop).
[0,0]; [1,0]; [2,0]; [3,0];....... [28,0]; (First column).
[0,1]; [1,1]; [2,1]; [3,1];....... [28,1]; (Second column).
Where the first numbers represent the rows and the second numbers represents the columns in at table.
The idea being the row variable can be incremented each time in the the loop by 1.
First I need to find a way of setting up variables and then combining them together to get the variable: cellindex.
Unless there is a better way to do this?
Whatever I try, I keep getting not defined errors or cannot read property 'undefined' of undefined, Unexpected token etc...
I have included some of my poor attempts at building this below (commented in the code).
//var row = 0;
//var column = 0;
//var cellIndex = [row,column];
//var cellIndex = [row + "," + column];
//var cellindex = "[ + row + "," + column + ]";
//var cellindex = "[ + row + ',' + column + ]";
//var cellIndex = [+row+","+column+];
//var cellIndex = [ +row+ +','+ +column+ ];
var cellIndex = [0,0];
var cellData = sheet.getCellState(cellIndex);
var celldata = "";
celldata += (cellData);
console.log('Cell Data: ' + celldata);
Well the below works but it is huge code and has to be repeated for all 7 columns.
I cannot figure it out and if no-one else knows how to how to build up the the cellindex value with code then I cannot even attempt a loop to shorten it.
var cellIndex = [0,0];
var cellData = sheet.getCellState(cellIndex);
var celldata = "";
celldata += (cellData);
cellIndex = [1,0];
cellData = + sheet.getCellState(cellIndex);
var celldata = celldata;
celldata += (cellData);
cellIndex = [2,0];
cellData = + sheet.getCellState(cellIndex);
var celldata = celldata;
celldata += (cellData);
cellIndex = [3,0];
cellData = + sheet.getCellState(cellIndex);
var celldata = celldata;
celldata += (cellData);
//Repeat another 25 times
console.log('Cell Data: ' + celldata);
This is answered now, thanks to lucas.
I can output the data for each column in the format I require. Which is, for example:
1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,0,1,0,0,0,1,0,0,0,0,1,1,1,1

If your cell index goes from [0,0] to [27,6] maybe this will help
var data ={};
for (var i=0;i<28;i++) {
for (var j=0;j<7;j++) {
var cellIndex = [i,j];
if(!data["col"+j]){
data["col"+j]=[]
}
var cellIndex = [i,j];
var cellData = sheet.getCellState(cellIndex);
data["col"+j].push(cellData)
}
}
console.log(data)
console.log(data.col1) // data.col1 will give you an array of column 1 values for further processing
console.log(data.col1.toString()) // this will give you a string you can copy from the console to paste elsewhere
If you wanted to split the data out into rows or columns you would just output an array (or something) at the end of each loop iteration

What about this
for (let i=0;i<28;++i) {
for (let j=0;j<28;++j) {
sheet.setCellState([i,j],[i,j]);
}
}

Checkout arrays in javascript since is the type of data you are presenting.
https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array

Related

Google Sheets - Delete values of range if I have 0 in values of certain column

I would like to remove values in column B and C if in the column B I have cells with value 0.
I have tried this based on other code on this forum, with the method push to fill a empty range. The problem is when I clearContent on both arrays (values of B and C) it cleans all my values even if they have values different from zero.
I try to search the error with fontsize my second range(values of C) and it works.
My code is :
function clean0() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getRange('B:B').getDisplayValues();
var range = [];
var rangeC= [];
data.forEach(function(e, i){
if (e[0] == "0"){
range.push("B" + (i + 1));
rangeC.push("C" + (i + 1));
}
});
sheet.getRangeList(range).clearContent();
sheet.getRangeList(rangeC).clearContent();
function clean0() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('B:C');
var data = range.getValues();
var formulas=range.getFormulas();
data.forEach(function(row, index){
//console.log('Index:'+ index + 'row' + row);
row.forEach(function(amount,index1,array){
//console.log('Row: ' + array);
var index2=row.slice(0,1);
if (row[0] == 0 && row[1]>0){
sheet.getRange(row).clear({commentsOnly: true});
//console.log('Row after: ' + row)
//data[index][index1] = "";
//console.log(data)
}
});
});
range.setFormulas(formulas)
//range.setValues(data)
}
You may try the following edited code for you, i think is much better than using push function to clear a content, although I did not use clearContent in this case, === to prevent clear range if empty:
function clean0() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('B:D');
var data = range.getValues();
data.forEach(function(row){
if (row[0] === 0){
row[0] ='';
row[1] ='';
}
});
range.setValues(data)
}

Concatenate a dynamic Number of columns Google Script

I need to concatenate x number of columns per project sometimes it's 3 columns others 7 or 5, it just depends
I am trying to this with an array of range column numbers ex [2,5,3]
columns 2,5 3 in that order with a delimiter here |
I have searched but found only static concatenating functions
I have a VBA Macro that works as I need in Excel so I am trying to write it in Google Script
The function runs without erroring but nothing is posted back
From Logger.log() I am kinda close to the proper structure
I get undefined|b|e|c
I want to post back to the last column + 1
I am not sure this is the best way to do this but it what I have
Any help is appreciated, Thanks
colA ColB ColC ColD ColE ColF ColG ColH
a b cc d e f g b|e|c
a2 b2 d2 e2 f2 g2 e2|c2
ect.
Here is what I have:
function TemplateA_n() {
Template_A("A", [2, 4, 6])
}
function Template_A(SshtName, sArr){
var sSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SshtName);
var sR = sSheet.getDataRange();
var sV = sR.getValues();
var sLC = sSheet.getLastColumn();
var sLR = sSheet.getLastRow();
var a = []
//Rows
for (var row = 2; row < sLR; row++){
a[row] =[]
//Columns
for (var col = 0; col < sArr.length ; col++){
if(sV[row][sArr[col]] !== "") {
if(sV[row][sArr[0]] == "") {
a[row][0] = a[row][0] + sV[row][sArr[col]];
Logger.log(a[row][0])
}
else {
a[row][0] = a[row][0] + "|" + sV[row][sArr[col]];
Logger.log(a[row][0])
}
}
}
}
sSheet.getRange(1,sLC + 1,sLR,1);
}
Here is the Macro
Sub ConCatA()
Dim rng As Range, r As Range, i As Long
On Error Resume Next
Set rng = Application.InputBox("Select column(s)", Type:=8)
'Set rng = Range("B1,A1,C1")
On Error GoTo 0
If rng Is Nothing Then Exit Sub
With ActiveSheet.UsedRange
ReDim a(1 To .Rows.Count, 1 To 1)
a(1, 1) = "Concat"
For i = 2 To .Rows.Count
For Each r In rng
If .Cells(i, r.Column) <> "" Then
a(i, 1) = a(i, 1) & IIf(a(i, 1) = "", "", "|") & .Cells(i, r.Column).value
End If
Next r
Next i
With .Offset(, .Columns.Count).Resize(, 1)
.value = a
End With
End With
End Sub
Part 1: Undefined value in output
The reason you get the following ouput: undefined|b|e|c is because the variable a[row][0] is undefined before you assign it any value. So when program runs the following line of code for the first time in the loop it concats the value of sV[row][sArr[col]]to undefined.
a[row][0] = a[row][0] + sV[row][sArr[col]]
All you need to do is assign an empty value to begin with, like so
for (var row = 2; row < sLR; row++){
a[row] =[]
a[row][0] = ""
... your code here
}
Also, since the assignment of values only start from index 2 in the loop, we need to assign index 0 and 1.
a[0] = [""]
a[1] = [""]
This will enable us to input blank values in the sheet when we use setvalues function with this array.
Part 2: Append values to sheet (lastColumn + 1)You define the range to append your data and then set its values, as follows:
var appRange = Sheet.getRange(2,sLC+1,a.length,1)
appRange.setValues(a)
Your final code would look like this:
function Template_A(SshtName, sArr){
var sSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SshtName);
var sR = sSheet.getDataRange();
var sV = sR.getValues();
var sLC = sSheet.getLastColumn();
var sLR = sSheet.getLastRow();
var a = []
//Rows
a[0]= [""] // Since you are start from index 1, you need to assign a value to index 0
for (var row = 1; row < sLR; row++){ //If you intended to start from 2nd row
// var row = 1 and not 2
a[row] = []
a[row][0] = "" //Empty value for each row
//Columns
for (var col = 0; col < sArr.length ; col++){
if(sV[row][sArr[col]-1] !== "") {
if(a[row][0] == "") { //Check to see if the array is empty
// If yes donot add "|"
a[row][0] = a[row][0] + sV[row][sArr[col]-1];
Logger.log(a[row][0])
}
else {
a[row][0] = a[row][0] + "|" + sV[row][sArr[col]-1];
Logger.log(a[row][0])
}
}
}
}
Logger.log(a)
var appRange = sSheet.getRange(1,sLC+1,a.length,1)
appRange.setValues(a)
}
Final Note: If you intend to skip the first row in your sheet your loop should start with counter 1. Since array index starts from 0 but row numbering in sheet start from 1.

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.

How can I merge Same Range from each file in a folder onto a master sheet?

I modified the code this way... it gathers data from all the sheets and finds only the rows that have data, BUT now I am having a problem modifying the range with each pass so that it is equal to the number of rows that do have value (found with (values[row][0] != '')). I have put a ??? in the spot where I am trying to have a variable height.
function getAllData() {
var folder = DocsList.getFolderById("folderid");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Base")
sheet.clearContents();
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet 1");
var lastLine = sheet2.getLastRow();
var values = sheet2.getRange('A3:J').getValues();
var formulas = sheet2.getRange('A3:J').getFormulas();
var data = [];
for(var row = 0 ; row < (values).length ; row++){
var lastrow = sheet.getLastRow()+1;
if (values[row][0] != '') {
for(var col = 0 ; col < formulas[row].length ; col++){
if(formulas[row][col] != '')
{values[row][col] = formulas[row][col]};
data.push(values[row]);}
if(data.length > 0)
sheet.getRange(lastrow, 1, ???, data[0].length).setValues(data);
}
}
};
}}
You are using getValue() as opposed to getValues() (With a letter "s" on the end)
var onecell = posheet.getRange('B4').getValue();
The documentation states:
getValue() - Returns the value of the top-left cell in the range.
The parameter for getRange() is kind of tricky and not well documented.
For example this:
getRange(2, 3, 6, 4)
gets a range from C2 to G8. Figure that out. The first number is the number 2, which is for the row 2. The second number is 3, for the third column (which is C). The third and fourth numbers are relative to the first two numbers.
Also, you are using: appendRow([array]) which uses an array for the parameter. So you must make sure that the data is in the form of an array, or use something else.
Here is the link for getValues:
Google Documentation - getValues
The example is this code:
// The code below will get the values for the range C2:G8
// in the active spreadsheet. Note that this will be a javascript array.
var values = SpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 4).getValues();
Logger.log(values[0][0]);
Here is code that seems to work:
function getAllData() {
var folder = DocsList.getFolderById("Your file ID");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1")
sheet.clearContents();
sheet.appendRow(["Value from Sheet One", "Range of values from Sheet Two"]);
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
//Reset to null on every iteration
var onecell = null;
var theRange = null;
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet1 = SpreadsheetApp.open(file).getSheetByName("Sheet1");
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet2");
// The code below will get the values for the range A3:A9
// in the active spreadsheet. Note that this will be a javascript array.
onecell = sheet1.getRange('B4').getValue();
theRange = sheet2.getRange(1,3,1,6).getValues();
Logger.log('onecell: ' + onecell);
Logger.log('onecell[0][0]: ' + onecell[0][0]);
Logger.log('theRange: ' + theRange)
Logger.log('theRange[0][0]: ' + theRange[0][0])
var multipleValues = [theRange[0][0], theRange[0][1], theRange[0][2], theRange[0][3], theRange[0][4]];
Logger.log('multipleValues: ' + multipleValues);
sheet.appendRow([onecell, "'" + multipleValues]);
};
}
}
In the first column, it only enters one value into the sheet cell. In the second column, the cell gets multiple values put into it from the row. In other words, and entire rows values, and combined and put into one cell. I think that's what you want from the code.
If you try to put an array into a spreadsheet cell, instead of showing the array of values as text, it shows something like an object. So I put a quote in front of the values so the cell formatting would default to text.

Trouble with JavaScript arithmetic and NaN values from the DOM

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);

Categories