Why does this for loop not work in Google scripts - javascript

i wrote the following loop based on the code found here:
How do I iterate through table rows and cells in javascript?
function myRowLooper() {
var inputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('INPUT');
var inputRange = inputSheet.getRange(2,inputSheet.getLastColumn(),inputSheet.getLastRow());
for (var i = 0, row; row = inputRange.rows[i]; i++) {
Logger.log(row);
for (var j = 0, col; col = row.cells[j]; j++) {
Logger.log(col);
}
}
}
but when I apply it to Google scripts it throws an error: "TypeError: Cannot read property "0" from undefined."
What's causing this?

Because you can't get any value from 'inputRange.rows[i]'.
You may do something like this :
function myRowLooper() {
var inputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
for(var i = 1; i<=inputSheet.getLastRow(); i++){
for(var j = 1; j<=inputSheet.getLastColumn(); j++){
var cell = inputSheet.getRange(i,j).getValue();
Logger.log(cell);
}
}
}
Hope this will help you.
Thanks.

Your code is extremely sloppy. You are trying to combine variables and condense unnecessarily and it's leading to errors in your code. There's no use in added "efficiency" if it leads to errors and mistakes.
Try something like this --
function yourFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Name");
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getMaxColumns();
var conditionToCheckColumn = (lastColumn - 1);
var conditionRange = sheet.getRange(2, checkColumn, (lastRow - 1), 1);
var check = checkRange.getValues();
for (var i = 0; i < check.length; i++) {
if (check[i] == condition) {
continue;
} else {
//your code here
}
}
}
This will pull a range at which you can check it's value/ condition and if matches, it does nothing. If it does not match, it will perform code. It will then loop to the next row until the last row in your check range that has data.
Be warned - functions count as data despite the cell being visibly empty. If your sheet uses functions like =QUERY, you will have an infinitely looping code unless your =QUERY (or other fx()) has a specific upper limit.

Related

How to get row number of an array that's being looped through?

Using Google AppScripts and found this script online which I've modified.
NOTE: Spreadsheet has headers.
Run through the values found in 3rd column
If any of the values include the word "USD", copy the entire row into "Target Sheet"
Delete that row after it's finished copying
Continuing looping through until it finds the next "USD"...etc.
What Works:
I'm successfully able to loop through the array and copy the correct rows into the "Target Sheet"
What I Need Help With:
I can't figure out how to delete the row from the original sheet. It always ends up deleting the row before, then skips 1 row every time it loops again. I've tried multiple different formats to this portion of the code, such as i-1, i+1, etc... Not sure what I'm doing wrong here:
if (i == 0) {
ss1.deleteRow(i+2);
} else {
ss1.deleteRow(i)
}
I've pasted the entire script below:
var etfar = ["Cash File"] //This is a string because I have multiple sheets I'm looping through
function cashCopy(etf) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ss1 = ss.getSheetByName(etf);
var ss2 = ss.getSheetByName("Target Sheet");
var lr = ss1.getLastRow();
var lc = ss1.getLastColumn();
// gets the data in Cash File
var range = ss1.getRange(1, 1, lr, lc);
var data = range.getValues();
// loops through rows in data
for (var i = 0; i < data.length; i++) {
var check = data[i][2] // ith row, 3rd column
if (check.includes("USD")) {
var rowToCopy = data[i];
ss2.appendRow(rowToCopy);
if (i == 0) {
ss1.deleteRow(i+2);
} else {
ss1.deleteRow(i)
}
};
}; // end i
}
for (var i = 0; i < etfar.length; i++) {
cashCopy(etfar[i])
}
Explanation:
One way to iteratively delete rows in a sheet is to create a backwards for loop.
Replace:
for (var i = 0; i < data.length; i++)
with:
for (var i = data.length - 1; i >= 0; i--)
In this way, every time you delete a row, the data will still correspond to the correct row.
Another issue is that you get var range = ss1.getRange(1, 1, lr, lc). That means, you start iterating from the first row (including the headers) and then you are using a workaround like that:
if (i == 0) {
ss1.deleteRow(i+2);
} else {
ss1.deleteRow(i)
}
But actually, you don't need to include the headers in the first place. Use this instead: var range = ss1.getRange(2, 1, lr, lc) and replace the if/else statement with just: ss1.deleteRow(i+2).
since data index starts from 0, but your range starts from row 2.
Solution:
var etfar = ["Cash File"] //This is a string because I have multiple sheets I'm looping through
function cashCopy(etf) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ss1 = ss.getSheetByName(etf);
var ss2 = ss.getSheetByName("Target Sheet");
var lr = ss1.getLastRow();
var lc = ss1.getLastColumn();
// gets the data in Cash File
var range = ss1.getRange(2, 1, lr, lc); // <- modification
var data = range.getValues();
// loops through rows in data
for (var i = data.length - 1; i >= 0; i--)
{
var check = data[i][2] // ith row, 3rd column
if (check.includes("USD")) {
var rowToCopy = data[i];
ss2.appendRow(rowToCopy);
ss1.deleteRow(i+2); // <- new code
};
}; // end i
}
for (var i = 0; i < etfar.length; i++) {
cashCopy(etfar[i])
}

Getting 'activeSpreadsheet.getSheetByName()' to change dynamically

Please see the picture below
I have this GoogleSheets spreadsheet with a BUNCH of tabs (sheets) in it. The following code needs to run for EVERY sheet:
function SearchCols()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1"),
searchVal = sheet.getRange("L7").getValue(),
searchCol = sheet.getRange('L11:L30').getValues();
searchCol1 = sheet.getRange ('R11:R30').getValues();
searchCol2 = sheet.getRange('Q11:Q30').getValues();
searchCol3 = sheet.getRange('S11:S30').getValues();
searchCol4 = sheet.getRange('T11:T30').getValues();
for (var i = 0, len = searchCol2.length; i < len; i++)
for (var j = 0, len2 = searchCol1.length; j < len2; j++)
{
if (searchCol2[j][0] == searchVal && searchCol1[j][0] == searchCol[i]
[0])
{
sheet.getRange('N11:N10').setValue;
sheet.getRange(i + 11, 14).setValue(searchCol3[j][0])
sheet.getRange(i + 11, 15).setValue(searchCol4[j][0])
}
}
}
It just some code to loop through a column to see if any values match with another column, and if so, paste some data into adjacent cells.
The code works, BUT...
See the 3rd line of code? Where it says: ".getSheetByName("Sheet1")"???
How do I change that "Sheet1" part so it DYNAMICALLY knows what sheet I'm on in order to run the code FOR THAT SPECIFIC SHEET?
I have a 'Run' button in EVERY sheet of my file, but I ONLY want this code to run for the sheet that I press the button in. (look at the attached screenshot!)
The way it is right now, if I'm in Sheet50 and I press the button to run this code, it will produce the results in Sheet1, but I need it to produce results in Sheet50! The same applies for every sheet of my file. When I press the 'Run' button, I need the code to ONLY run for that specific sheet.
Please help, dear friends!
To loop through all of your sheets:
Get all of the sheets in the spreadsheet using .getSheets() then use a for loop to iterate through all of the sheets in the spreadsheet.
This is your code modified to work as you asked:
function SearchCols() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
var sheet = ss.getSheetByName(sheets[i].getName());
var searchVal = sheet.getRange("L7").getValue();
var searchCol = sheet.getRange('L11:L30').getValues();
var searchCol1 = sheet.getRange('R11:R30').getValues();
var searchCol2 = sheet.getRange('Q11:Q30').getValues();
var searchCol3 = sheet.getRange('S11:S30').getValues();
var searchCol4 = sheet.getRange('T11:T30').getValues();
for (var x = 0, len = searchCol2.length; x < len; x++) {
for (var j = 0, len2 = searchCol1.length; j < len2; j++) {
if (searchCol2[j][0] == searchVal && searchCol1[j][0] == searchCol[x][0]) {
sheet.getRange('N11:N10').setValue;
sheet.getRange(x + 11, 14).setValue(searchCol3[j][0])
sheet.getRange(x + 11, 15).setValue(searchCol4[j][0])
}
}
}
}
}

How do you use a script to delete a row off of a spreadsheet that is linked to a form?

I have this script that does several things with data once it is entered into a google form, but I need to make sue that when two entrants have the exact same name that it deletes the previous entry entirely.
function formChanger() {
var doc = DocumentApp.openById('THIS WAS MY ID');
var body = doc.getBody();
var date = body.getListItems();
var dates = [];
for(var i = 0; i<date.length;i++)
{
dates.push(date[i].getText());
}
var form = FormApp.openById('THIS WAS MY ID');
var items = form.getItems();
var ss = SpreadsheetApp.openById("THIS WAS MY ID");
Logger.log(ss.getName());
var sheet = ss.getSheets()[0];
var values = sheet.getSheetValues(2, 4, sheet.getLastRow() , 1);
Logger.log(values);
var names = sheet.getSheetValues(2, 2, sheet.getLastRow(), 1);
var item = items[2].asMultipleChoiceItem();
var choices = item.getChoices()
for(var i=names.length; i>-1; i--){
for(var j=names.length; j>-1; j--){
if(names[i]==names[j] && i != j)
sheet.deleteRow(i);
}
}
var h = -1;
var j = -1;
var k = -1;
var l = -1;
for(var o = 0; o<values.length; o++){
if(choices[0].getValue().equals(values[o].toString()))
h++;
if(choices[1].getValue().equals(values[o].toString()))
j++;
if(choices[2].getValue().equals(values[o].toString()))
k++;
if(choices[3].getValue().equals(values[o].toString()))
l++;
}
if(h>3)
dates.splice(0,1);
if(j>3)
dates.splice(1, 1);
if(k>3)
dates.splice(2, 1);
if(l>3)
dates.splice(3, 1);
emptyDocument();
Logger.log(h);
Logger.log(j);
Logger.log(k);
Logger.log(l);
item.setChoices([
item.createChoice(dates[0]),
item.createChoice(dates[1]),
item.createChoice(dates[2]),
item.createChoice(dates[3])
]);
for(var i = 0; i<dates.length; i++)
body.appendListItem(dates[i]);
Logger.log(doc.getName()+" Contains:");
Logger.log(dates);
}
Yes the code is a mess, and I'm sure that it could be done a better way, but the important part is that I could be able to delete the line of information that is repeated. The compiler will not allow me to do this because the Spread Sheet is linked to the form. is there a way around this?
The following attempts at deletion are blocked in sheets receiving form data:
deletion of columns with form data
deletion of the row with form questions - that is, row 1
Other rows can be deleted at will. This behavior is exactly the same for scripts as it is for user actions.
Your script attempts to delete row 1 because it's buggy. I quote the relevant part:
var names = sheet.getSheetValues(2, 2, sheet.getLastRow(), 1);
for(var i=names.length; i>-1; i++){
for(var j=names.length; j>-1; j++){
if(names[i]==names[j] && i != j)
sheet.deleteRow(i);
What row is names[i] in? It's in row i+2, because i=0 corresponds to row 2. Yet, you attempt to delete row numbered i, two rows above the intended one.
Besides, i>-1; i++ is absurd; you want i-- there.
Here is a simple script that deletes row with duplicates; it's tested with my form responses. It traverses the contents of "Form Responses 1" sheet from bottom to top; if two rows have the same value in column C, the older one gets deleted. I do take care not to attempt deletion of row 1.
(The reason to do this in bottom-up order is to avoid dealing with rows that moved up because others were deleted.)
function deleteDupes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Form Responses 1');
var values = sheet.getDataRange().getValues();
for (var i = values.length - 1; i > 1; i--) {
if (values[i][2] == values[i-1][2]) {
sheet.deleteRow(i);
}
}
}

How to Setvalues off of an array google GAS for spreadsheets

I have been using google scripts for a week, and I have searched as much as I could to get the answer. Can someone please help me? I wrote a simple script to evaluate if a course is online based on the last three digits of a course number(i.e PSY-250-400). The script works fine, and I pushed the result into the end of the array. I don't know how to write back to google sheets. Below is what I have. Currently it will set the values based on the first result(online course). So all values are set to online. I am running it on 7 rows right now, but will need to run it on 20,000.
function onlineonly(online){
var sheet = SpreadsheetApp.getActiveSheet();
var students = sheet.getRange('A2:D7').getValues();
var online = ["400","401","403","404","600"];
var m;
var section;
for(var i=0; i<students.length; ++i){
section = students[i][3].substring(8,13);
for(var j = 0;j<online.length; j++){
if(section===online[j]){
section = m;
}
}
if(section === m){
students[i].push("online");
} else {
students[i].push("not online");
}
var method = [];
for(var k = 0; k<students.length; k++){
if(students[i][4]=== "online"){
method = "online";
} else {
method = "in person";
}
sheet.getRange('c2:c7').setValue(method);
}
}
}
The important thing to remember is that the dimensions of the Range must equal the exact dimensions of the Array[][]. This array must be two-dimensional! Otherwise you'll get an error that setValues() method expects an Object[][], not an Array.
You're trying to set a simple array. Also, the method you'll use is setValues(), not setValue().
Your code is a little hard to understand, so this is an example of the pattern:
function writeOutValues() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange("C2:C7");
var values = range.getValues();
//remember, a 2d array is a grid of values that corresponds to the grid of the range like so: values[row][column]
//so in this range, values[0][0] = C2, values[1][0] = C3, etc.
values[0][0] = "New Value";
values[1][0] = "Another one";
//to set value,
range.setValues(values);
}
just move inner for loop
for(var k = 0; k<students.length; k++) to outside the main for loop
and apply the technique as told by zbnrg
here is working code
function onlineonly()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var students = sheet.getRange('A2:C4').getValues();
var online = ["400","401","403","404","600"];
var m;
var section;
for(var i=0; i<students.length; ++i)
{
section = students[i][1].slice(8,11);
for(var j = 0;j<online.length; j++)
{
if(section===online[j])
{
section = m;
}
}
if(section === m)
{
students[i].push("online");
}
else
{
students[i].push("not online");
}
}
var range = sheet.getActiveSheet().getRange("C2:C4");
var method = range.getValues();
for(var k = 0; k<students.length; k++)
method[k][0] = students[k][3]==="online"?"online":"in person";
Logger.log(method[0][0] +" "+method[1][0] +" "+ method[2][0])
range.setValues(method);
}
here is my spreadsheet https://docs.google.com/spreadsheets/d/1iA9v_3rPH9JAhAmt2EJTdbqTQkFEl7yewawPy3w4YIg/edit#gid=0

Google Spreadsheet Script: "Cannot find function getRange in object Sheet" when creating a simple function

Sorry, for the stupid question, but I´ve searched the whole internet and I could not find a good Tutorial to learn how to program in Google SpreadSheet Script.
I want to make a very simple function just for practice.
function simplesum(input) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets();
var range = sheet.getRange(input);
var x = 0;
for (var i = 1; i <= range.getNumRows(); i++) {
for (var j = 1; j <= range.getNumColumns(); j++) {
var cell = range.getCell(i, j);
x += (cell.getValue());
}
}
return x;
}
I know I could use =sum() to do exactly the same thing. The idea here is to learn how to program.
When I try to use my function in a cell: (i.e: =simplesum((A1:A8)) it gives an Error saying: "TypeError: Cannot find function getRange in object Sheet. (line 4)"
What should I do?
And again, sorry for the dumb question....
In this case, you are implementing a Google Apps Script function as a custom function, invoked in a spreadsheet cell.
When you pass a range to a custom function invoked in a spreadsheet cell, you are not passing a range object or a range reference, but rather a 2-D Javascript array of values. So your custom function should just process that array.
function simplesum(input)
{
var x = 0;
for (var i = 0; i < input.length; i++)
{
for (var j = 0; j < input[0].length; j++)
{
x += input[i][j];
}
}
return x;
}
This is working :
function sum(input) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet(); // your mistake : getSheets()
var range = sheet.getRange(input);
var x = 0;
for (var i = 1; i <= range.getNumRows(); i++) {
for (var j = 1; j <= range.getNumColumns(); j++) {
var cell = range.getCell(i, j);
x += cell.getValue();
}
}
return x;
}
function main () // Yes, I am a former C-programmer ...
{
var s = sum ("A1:B3"); // Notice the quotes. A string must me entered here.
Logger.log('s = ' + s);
}
var sheet = ss.getSheets();
returns an Array of sheets, meaning sheets.getRange(input) will throw that error. Try this instead:
var sheet = ss.getSheets()[0];
which selects the first sheet of the array of sheets. Google has some decent documentation for this. For example, here's its documentation on getRange(). Note that it uses ss.getSheets()[0] as well. Hope this helped!

Categories

Resources