google sheet script doesn't enter the second for loop - google-apps-script

I'm doing a loop in google sheets script, i'm trying to find a row in the "Inventar" sheet that has the same value found in the sheet "Fisa client" and then change the value of a cell in that row but it appears that it does not enter the second for. I'm new to this so i don't know what's the problem.
function adaugabucati1(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var ss=sheet.getSheetByName('Fisa client');
var ss2=sheet.getSheetByName('Inventar');
var data = ss.getRange("g9:l18").getValues();
var data1=ss2.getDataRange().getValue();
for(var i=0;i<data.length;i++){
if(!data[i][0] == ""){
var cod = ss.getRange(i+9,8).getValue();
var buc = ss.getRange(i+9,10).getValue();
for(var y=0;y<data1.length;y++){
if(data1[y][5] == cod){
var bucati = data[y][7]-buc;
ss2.getRange(y+1,8).setValue(bucati);
}
}
}
}
}

The problem is in this line:
var data1=ss2.getDataRange().getValue();
getValue() returns only one value - the value of the top-left cell in the range.
When the if statement following the second loop evaluates data1[y][5], it returns "undefined" and the rest of the second loop is bypassed.
You need to change line#6 to:
var data1=ss2.getDataRange().getValues();
Note getValue() - > getValues()

Related

How to set formula with condition if the cell is blank with google app script?

So I have multiple files that have a column where I would like to update in the formula. However, there might be a certain cell that already has a value in it, but I don't want to replace it with the formula (see screenshot for reference).
I read some references here, but haven't found a similar case like mine.
This is the attempt that I do, but it's not working:
function updateWithFormula(){
/*** Input Data From Multiple Sources ****/
var sourceWorkbook = SpreadsheetApp.openById('')//id of the workbook
//Open tab 'Sheet1' and pull the data inside the script
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1')
var source = sourceSheet.getDataRange().getDisplayValues()
for(row in source){
if (source[row][3]=="Update Value") {
//open files through link
var files = SpreadsheetApp.openByUrl(source[row][2]) //there's a link inside this column that linked to the file that I want to update
/*******insert formula *******/
//get range that want to be inserted by the formula, which is column S
//if the column S already have value in it, I don't want to do anything in it, however if it doesn't have value, I would like to put a formula
var result = files.getSheetByName('Sheet1').getRange("S2:S") //this is the column that I want to update
//set formula
for(r in result)
{
if(result[r] == "")
result[r].setFormula("=R"+ r+1)
}
}
}
}
Do you guys have any idea why my code is not working? Any advice for this case?
Thank you!
Objective
If I understood correctly, your objectives are the following:
Retrieve data from a "master" spreadsheet with information on which spreadsheets to update.
Loop through said data and locate the spreadsheets (represented as rows) that require updating.
Open those spreadsheets individually.
Update those spreadsheets rows with a sheets formula if a certain condition is met (in this case, that the cell is blank).
Issues
The for(var a in b) syntax in javaScript is used to iterate through object, not arrays. You should change it to:
for (var i = 0; i<source.length; i++){
//YOUR CODE
}
where: source[i] lets you access that specific row.
When you try to get the individual sheets' values, you are actually only getting the range, not the values themselves. You should replace this:
var result = files.getSheetByName('Sheet1').getRange("S2:S")
with this:
var sheet = files.getSheetByName('Sheet1');
var range = sheet.getRange("S2:S");
var values = range.getValues();
(You can read more about ranges and how they work here).
To input values into a spreadsheet, you should do it by using the setValue() method in the range class. Again, go here for more info. So, instead of:
result[r].setFormula("=R"+ r+1)
use:
var rangeToModify = sheet.getRange(j, 19); //LETTER S IS THE 19TH
rangeToModify.setValue("=R"+ (j+1)); //SET THE FORMULA
Final Code
function updateWithFormula(){
var sourceWorkbook = SpreadsheetApp.openById('')//id of the workbook
//Open tab 'Sheet1' and pull the data inside the script
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1')
var source = sourceSheet.getDataRange().getDisplayValues()
for(var i = 0; i<source.length; i++){
if (source[i][3]=="Update Value"){
var files = SpreadsheetApp.openByUrl(source[row][2]);
var sheet = files.getSheetByName('Sheet1');
var range = sheet.getRange("S2:S");
var values = range.getValues();
//set formula
for(var j = 0; j<values.length; j++){
if (values[j] == ""){
//GET THE RANGE THAT YOU WANT TO MODIFY
var rangeToModify = sheet.getRange(j, 19); //LETTER S IS THE 19TH
rangeToModify.setValue("=R"+ (j+1)); //SET THE FORMULA
}
}
}
}
}
I believe your current situation and your goal are as follows.
"Sheet1" of sourceWorkbook has the Spreadsheet URLs and the value of "Update Value" in the columns "C" and "D", respectively.
You want to retrieve the Spreadsheet from the URL, and want to check the column "S2:S" of of "Sheet1" in the retrieved Spreadsheet, and want to put a formula like "=R"+ r+1 to the non-empty cells of the column "S".
In this case, how about the following modification?
Modification points:
var result = files.getSheetByName('Sheet1').getRange("S2:S") returns Class Range object. This cannot be used with for(r in result). This is the reason of but it's not working. This has already been mentioned by the Oriol Castander's answer.
When setFormula is used in a loop, the process cost becomes high.
When these points are reflected in your script, it becomes as follows.
Modified script:
function updateWithFormula() {
var sourceWorkbook = SpreadsheetApp.openById(''); // Please set your Spreadsheet ID.
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1');
var source = sourceSheet.getDataRange().getDisplayValues();
source.forEach(r => {
if (r[3] == "Update Value") {
var sheet = SpreadsheetApp.openByUrl(r[2]).getSheetByName("Sheet1");
var rangeList = sheet.getRange("S2:S" + sheet.getLastRow()).getDisplayValues().flatMap(([e], i) => e == "" ? [`S${i + 2}`] : []);
if (rangeList.length > 0) {
sheet.getRangeList(rangeList).setFormulaR1C1("=R[0]C[-1]");
}
}
});
}
In this modification, the formula is put as the R1C1 using the range list. By this, I thought that the process cost will be able to be reduced a little.
References:
getRangeList(a1Notations)
setFormulaR1C1(formula)

Get notes from a cell and insert into another cell

I have a range of cells in Google Sheets, some of them have notes attached.
If there's a note attached to a cell, I need to put the note in a separate cell, and put the location of that note in another cell.
I found this script elsewhere:
function getNote(cell)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange(cell)
return range.getNote();
}
but when I try to use it I get an error "Exception: Range not found (line 12)."
But this script only gets me halfway there as it only gets the note and puts it in a cell. I also need to know what cell the note came from.
Any help is greatly appreciated.
In the script above the function getNote() expects the paramter cell
If you just run the script without calling getNote() from another function / an environment where it gets a values for cell assigned, the script will fail wiht the error you obtained.
Indeed, this script doe snot not meet your needs. What you probably want is to screen all your cells for the one that have notes.
What you need to decide is into which cells you want to put the note and the cell notation.
Below is a sample that you need to adapt for your needs:
function getNotes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//if you have only one sheet in the spreadsheet, otherwise use ss.getSheetByName(name);
var sheet = ss.getActiveSheet();
var range = sheet.getDataRange();
var results = range.getNotes();
for (var i = 0; i < results.length; i++) {
for (var j = 0; j < results[0].length; j++) {
//if a not empty note was found:
if(results[i][j]){
var note = results[i][j];
var cell = range.getCell(i+1, j+1);
var notation = cell.getA1Notation();
//adjust the offset as function of the column / row where you want to output the results
cell.offset(0, 1).setValue(note);
cell.offset(0, 2).setValue(notation);
}
}
}
}
Important references:
getNotes()
getA1Notation()
getDataRange()
getCell(row, column)
offset(rowOffset, columnOffset)

Google Sheets Script - Reference a specific cell in a row

I have a sheet where when I change a specific cell to "YES", I need a template sheet to be copied to a new version and named as per the value of a cell on the current row.
I'm having trouble working out how to get the value of the first cell in the row selected. This is what I have so far (I know this is wrong):
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = sheet.getCurrentCell();
if (currentCell = "YES")
{
SpreadsheetApp.getActiveSpreadsheet().toast("New change control sheet added to workbook.","Change Control",15);
var sourceRow = ss.getActiveRange().getRowIndex();
var tabName = ss.getRange(cell,1).getValues();
ss.getSheetByName("CCTemplate").showSheet()
.activate();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.duplicateActiveSheet();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.getActiveSheet().hideSheet();
ss.setActiveSheet(ss.getSheetByName('Copy of CCTemplate'), true);
ss.getActiveSheet().setName("CC" & tabName);
}
}
Any ideas?
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()=='Your Sheet Name' && e.value=="YES") {
e.source.toast="New change control sheet added to workbook.","Change Control",15);
var tabName=sh.getRange(e.range.rowStart,1).getValue();
var tsh=e.source.getSheetByName('CCTemplate');
var csh=tsh.copyTo(e.source);
csh.setName('CC'+tabName);
}
}
You should avoid using activate in your scripts especially in simple triggers where you have to finish in 30 seconds. I think this code does the same thing that you intended for your code. One significant difference is that I use the information that comes in the event object that comes with the trigger. You should add the code Logger.log(JSON.stringify(e)) and then look at the logs you will see that there is a lot of information available to you which removes the need to run extra functions to get things like a spreadsheet.
Use event objects
onEdit offers among others the event objects range and value which are helpful to retrieve the range that has been edited and its value.
Also
When you want to a cell and compare it against a value, like in if (currentCell = "YES") - you need to retrive its value (either currentCell.getValue() or just event.value) and you need to use == instead of = for comparison.
Be careful with getValues() vs getValue(). The former gives you a 2D array and is not necessary if you want to retrieve the value of a single cell.
There is no need to set your sheet to active in order to change its name.
You can rewrite your code as following:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = event.range;
var value = event.value;
if (value == "YES")
{
...
var sourceRow = range.getRowIndex();
var tabName = ss.getRange(sourceRow, 1).getValue();
...
ss.getSheetByName('Copy of CCTemplate').setName("CC" + tabName);
}
}

Getting Last Row of Data, Ignoring Formulas and Use Row Data to send email - Google Apps Script

I'm collecting data from a Google Form that will be used in formulas that calculate costs and mileage. I've used =QUERY to bring the responses to another sheet 'Event Calculator'. When I run the forEach loop to get the data, it picks up cells with a formula.
I would like my code to find the last row with data and pull some numbers from cells to use in an email that will be sent right after they submit the form (on a trigger).
Is there a way to find the last row and then run the forEach loop? or am I going about this all wrong?
I've tried using if statements and forEach, but don't seem to have the correct order and am unable to just find the last row with data in it, not a formula.
The expected results are a single line of data that can be placed in an email function to send the results of the formulas to the respondent.
I've tried using the forEach loop, but it returns rows that contain formulas. I have tried if statements but cannot seem to get it to work with the forEach loop to get the data.
function travelReport() {
// get data from the Sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Event Calculator');
var allRange = sheet.getDataRange();
var allData = allRange.getValues();
// remove the header row
allData.shift();
// loop over rows of data
allData.forEach(function(row) {
// get data
var email = row[0];
var eventName = row[1];
var coordName = row[2];
var startName = row[3];
var destinationName = row[4];
function travelReport() {
// get data from the Sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Event Calculator');
var allRange = sheet.getDataRange();
var allData = allRange.getValues();
// remove the header row
allData.shift();
// loop over rows of data
allData.forEach(function(row) {
The results so far is that the forEach loop picks up all the rows I have a formula in. Several errors occur when I try to add code to find the last row with data.
Try this approach:
function travelReport(e) {
var ss=SpreadsheetApp.getActive();
Logger.log(e);
//I assume timestamp is actually e.values[0]. But you can just look at Logger.log to figure it out.
var email=e.values[1];
var eventName=e.values[2];
var coordName=e.values[3];
var startName=e.values[4]
var destinationName=e.values[5];
GmailApp.sendEmail(email, subject, body, options);//Presumably you know how to configure all of this
}
function createTrigger(name) {
var ss=SpreadsheetApp.getActive();
if(!isTrigger(name)) {
ScriptApp.newTrigger(name).forSpreadsheet(ss.getId()).onFormSubmit().create();
}
}
function isTrigger(funcName){
var r=false;
if(funcName){
var allTriggers=ScriptApp.getProjectTriggers();
for(var i=0;i<allTriggers.length;i++){
if(funcName==allTriggers[i].getHandlerFunction()){
r=true;
break;
}
}
}
return r;
}
Want a more precise answer...Give me more details.

Google sheets app script setting formula / value onEdit

I have the following code to try and set a lookup formula when a user deletes a cell by mistake:
function onEdit(e)
{
var ssA = SpreadsheetApp.getActive();
var ss = ssA.getSheetByName("Completed Work Requests")
var lastRow = ss.getLastRow();
var range = ss.getRange(5,6,lastRow,1);
var data = range.getValues();
for(var i=0;i<data.length;i++)
{
if(data[i][0] == "")//If true then it's blank
{
data[i][0]="=IFERROR(INDEX('Client lookup sheet'!C[-1]:C[-1],MATCH(R[0]C[-4],'Client lookup sheet'!C[-5]:C[-5],false)),\"\")"
range.setValue(data[i][0]);
}
}}
The problem I am having is the range.setValue(data[i][0]); part where I can see that it is setting the entire range to the value of data[i][0] in the previous line. The issue is that even if a user inputs a value manually, the onEdit function simply resets the value to data[i][0] = .... while I want them to be able to set the manual value without the function overwriting the value.
So my question boils down to what do I need to use instead of range.setValue(data[i][0]) to ensure only the cell evaluated by data[i][0] == "" is set to become a formula rather than the entire range?
Thanks!
I have fixed my issues by editing my code to:
edit: The previous issue was that I couldn't figure out a way to set value when an instance of data[i][0] == "" was found. The original code in the question above had the setValue applied to the entire range so when a user manually inputted a value, the setValue would simply reset the value to the formula. I have found that I can find the range of data[i][0] using getRange(i+5,6) as the row starts at 5 and column number = 6. This new range can then be used to setValue at the appropriate cell rather than the whole range
function onEdit(e)
{
var ssA = SpreadsheetApp.getActive();//changed from openById() for my convenience
var ss = ssA.getSheetByName("Completed Work Requests")
var lastRow = ss.getLastRow();
var range = ss.getRange(5,6,lastRow,1);
var data = range.getValues();
for(var i=0;i<data.length;i++)
{
if(data[i][0] == "")//If true then it's blank
{
data[i][0]="=IFERROR(INDEX('Client lookup sheet'!C[-1]:C[-1],MATCH(R[0]C[-4],'Client lookup sheet'!C[-5]:C[-5],false)),\"\")"
var rangedata = ss.getRange(i+5,6)
rangedata.setValue(data[i][0]);
}
}}