Google Sheets appendRow with formula values - google-apps-script

My goal is to write a script in Google Sheets that will copy two (or more) values from specific cells on a single row on a worksheet to the bottom of the list on another. I need to copy the actual values as well as calculate additional from the original values.
This is my first attempt and should explain what I am trying to do:
function CopyCals() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Sheet2'), true);
spreadsheet.appendRow(['=today()','','=Sheet1!B30','=Sheet1!E30','=Sheet1!B30-Sheet1!E30','=Sheet1!B30-(Sheet1!E30-2500)']);
};
This works great, except it appends the actual formulas, rather than the values of the fields. As the fields change, so do the values of what was copied.
To work around this, I tried getRange but I can't figure out how to select specific elements (i.e. the values of C30 and E30) in the array. Below is what I tried:
function copyCals2() {
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange("B30:E30").getValues();
spreadsheet.appendRow(range[0]);
};
How do I get appendRow to only print the values for C30 and E30 and use them in formulas as shown in my original attempt.

Getting the desired data
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
};
This is probably easier to write:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var b30=sh1.getRange('B30').getValue();
var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
I would probably prefer doing it something like this:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getRange('B30:B31').getValues();
var b30=vA[0][0];
var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
If you had a lot of data and it's all grouped together nicely then the later can save you lot's of time because you only have to do one read to get all of the data at one time.
In the latter approach its helpful to use your debugger to help you to get a picture of what the data looks like:
vA=[[value in B30],[value in B31]] so the vA[0][0] is B30 and vA[1][0] is B31
And here is yet another way to write the getRange() function:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getRange(30,2,2,1).getValues();
//var vA=sh1.getRange('B30:B31').getValues();
var b30=vA[0][0];
var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
And just to make matters even a little more complicated for you a lot of times it's helpful to get the entire page of values all at one time so in that case this is what you left with.
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getDataRange().getValues();
var b30=vA[29][1];
var b31=vA[30][1];
//var vA=sh1.getRange('B30:B31').getValues();//gets all the data at one time
//var b30=vA[0][0];
//var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
All of these function do the same thing you can pick which one you want depending upon what your trying to accomplish.

Is there a way to do this if the row you are appending is not known?
For example I want to append a row with mixed data and a formula as such:
var values = [today, record['Date'], record['EventType'], record['Display Name'],
'=A?'];
var ss = SpreadsheetApp.openById(sponsorSheetId);
var sheet = ss.getSheetByName('SentLog');
sheet.appendRow(values);
Where, in A?, ? is the last row. But since I haven't appended yet, I wouldn't know what the last row is. And this needs to be a formula that refers to cell in column A.

Related

Apps script copy multiple range from 1 sheet to another spreadsheet

I am trying to copy data from 1 spreadsheet to another, I have successfully implemented something i found online that works with a specific range
function cloneGoogleSheet() {
// source doc
var sss = SpreadsheetApp.openById("spreadsheetkey1");
// source sheet
var ss = sss.getSheetByName('_tab_name_source');
// Get full range of data
var SRange = ss.getRange(7,3,5,1);
// get A1 notation identifying the range
var A1Range = SRange.getA1Notation();
// get the data values in range
var SData = SRange.getValues();
// target spreadsheet
var tss = SpreadsheetApp.openById("spreadsheetkey2");
// target sheet
var ts = tss.getSheetByName('tab_name_destination');
// Clear the Google Sheet before copy
//ts.clear({contentsOnly: true});
// set the target range to the values of the source data
ts.getRange(A1Range).setValues(SData);
};
The above piece coding work perfectly however I need to copy 18 different ranges that i cant just merge into 1 range. I considered the option of using the above however "multiplying" it 18 times for each range that however seems like a very inelegant solution.
I found a working solution that works if it stays within the same spreadsheet since it uses copyto instead of get/set values. The values part works perfectly since it doesnt mess with merge cells formatting. I have been struggling past 2-3 hours in merging the below-working code with elements from the first code to make a working script.
function test () {
try {
var spread = SpreadsheetApp.openById("spreadsheetkey");
var sheet = spread.getSheetByName("tab_name_source");
var rlist = sheet.getRangeList(["c7:c11", "g7:g11", "k7:k11"]);
sheet = spread.getSheetByName("tab_name_destination");
for( var i=0; i<rlist.getRanges().length; i++ ) {
var r1 = rlist.getRanges()[i];
var r2 = sheet.getRange(r1.getA1Notation());
r1.copyto(r2);
}
}
catch(err) {
Logger.log(err);
}
}
I tried initially to adapt the 2nd piece of coding to using setvalues however i had not been able to succesfully implement the part of getvalues within the scope of this code. I figured once I got this piece of code working with get and set values instead of Copyto i would only need to add the spreadsheetid of the other spreadsheet to get the final result
Try this:
function myFunction() {
var sourceSS = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = sourceSS.getSheetByName("sheetname");
var targetSS = SpreadsheetApp.openById("spreadsheet id here");
var targetSheet = targetSS.getSheetByName("Sheet1");
var ranges = ["C7:C11", "G7:G11", "K7:K11"];
ranges.forEach(range => {
var data = sourceSheet.getRange(range).getValues();
targetSheet.getRange(range).setValues(data);
})
}
Source sheet:
Destination sheet:
References:
setValues()
getValues()

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.

getActiveRange not returning current selection

This should be a simple one, but I could not crack it myself...
I want to copy the currently selected cells in the active sheet in an array called data:
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var selection = sheet.getActiveRange();
var data = selection.getValues();
The above results in the array getting the content of cell A1, no matter which cells are actually selected. When I replace getActiveRange() with e.g. getRange(2,1,10,10) that works as expected.
Any clue as to why I cannot get to the currently selected cells is appreciated!
It looks like the problem was on Google's side because after 24 hours of failure the existing code now works flawlessly. All your versions work fine too now.
I think that the problem is
var sheet = SpreadsheetApp.getActive().getActiveSheet();
This because there is some kind of bug when "chaining" methods of two different Object Classes, in this case Class Spreadsheet and Class Sheet. There is a bug report related to this getActiveSheet() returns first sheet name
The workaround is to replace the above line with:
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
Related
Why Class Range getValues sometimes returns [[]] when chained to Class Sheet getActiveRange?
This gets and displays the active range in a dialog window.
function getARange(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getActiveRange();
var vA=rg.getValues();
var s='';
for(var i=0;i<vA.length;i++){
s+=Utilities.formatString('<br />%s', vA[i].join(','));
}
var userInterface=HtmlService.createHtmlOutput(s);
SpreadsheetApp.getUi().showModelessDialog(userInterface, 'The Active Range')
}

Get 'cell reference out of range ' in Google Apps Script

So I've created a program that is supposed to mark one of multiple columns when the if conditions are met, so we can categorize our marketing sectors.
If you look at the last line of code you will see sheet (which encapsulates everything) getRange (which defines the range that the next part,getCell should be inside of ) and then you will see getCell.
I added +1 to the get cell Variable because it is not considered an array and therefore doesn't start with zero, which means it an sum of 4 would start in fourth row (as opposed to the 5th row for arrays).The problem is that I get a "Cell reference out of range and google script editor will highlight the aforementioned row.
If the get range is within the sheet, and the getCell is within the getRange I dont know how this could happen.
function myFunction() {
var app = SpreadsheetApp;
var sheet = app.getActiveSpreadsheet().getActiveSheet();
var lr= sheet.getLastRow();
var dataRangeContact = sheet.getRange("B4:B"+lr);
var dataContact = dataRangeContact.getValues();
var dataRangeComp= sheet.getRange("M4:M"+lr);
var dataComp=dataRangeComp.getValues();
var IndigoColumn= sheet.getRange("L4:L"+lr);
var validIndigoColumn =12;
var validLabColumn=11;
var validDesColumn=10;
var validPocColumn=9;
var validArtColumn=8;
for(var i=3; i<=lr; i++) {
var lr=sheet.getLastRow();
var rowTwo=dataContact[i].toString();
var rowThirteen=dataComp[i].toString();
if (rowThirteen=="University Lab") {
if (rowTwo.match(/(process engineer|analytical chemist|scientist|sr.scientist|research scientist|test Engineer|Medical Devices professional|scientific)/i)) {
sheet.getRange(4,12,lr).getCell(i+1,validIndigoColumn).setValue("Y");
}
}
}}
I think this is the same function rewritten a little differently. It will most likely run faster. It only has one range so it may be easier to avoid range errors. Keep in mind that rows and columns start at 1 and array indices start at 0.
I sometimes do this sort of thing to keep them straight.
Anyway here's my shortened version of the function:
function myFunction(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var vA=rg.getValues();
for(var i=3;i<vA.length;i++){
if(vA[i][12]=='University Lab' && vA[i][1].toString().match(/(process engineer|analytical chemist|scientist|sr.scientist|research scientist|test Engineer|Medical Devices professional|scientific)/i)){
vA[i][11]='Y';
}
}
rg.setValues(vA);//rewrites the data into the range including all changes all at one time
}
I have no way to test it.

Updating the formula for large numbers of sum cell across multiple sheets

I've got a spreadsheet with multiple formula like below (except for B4, C3 etc.)
=' NOV16'!B3+'DEC16'!B3+'JAN17'!B3+'FEB17'!B3+MARCH17!B3+'APR17'!B3+'MAY17'!B3+JUNE17!B3+JULY17!B3+AUGUST17!B3
I've created the script to create a new sheet and name it in the same format. I can't workout the best way to add to the formula.
Is it possible to do something like:
var newSheetName = newSheet
var data = ss.getRange(2,2,10,10).getFormula()
Then add the newSheetName onto each formula and write it to the sheet?
Or is there a better alternative.
Sorry there isn't much code to work on with this. I'm not sure where to even start with this part.
Try this:
function gatheringValues1()
{
var mySheets=['NOV16','DEC16','JAN17','FEB17','MARCH17','APR17','MAY17','JUNE17','JULY17','AUGUST17']
var ss=SpreadsheetApp.getActive();
var sht=ss.insertSheet('newSheet');
sht.activate();
var allSheets=ss.getSheets();
var sumB3s=0;
//var sumB3s='';
for(var i=0;i<allSheets.length;i++)
{
if(mySheets.indexOf(allSheets[i].getName())>-1)
{
sumB3s+=Number(allSheets[i].getRange('B3').getValue());//not sure which one you want
//sumB3s+=String(allSheets[i].getRange('B3').getValue());
}
sht.getRange('B3').setValue(sumB3s);
}
}
Or perhaps this was what you were after:
function gatheringFormulas()
{
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('oldSheet');
var rg1=sh1.getRange(2,2,10,10);
var rg1A=rg1.getFormulas();
var sh2=ss.getSheetByName('newSheet');
var rg2=sh2.getRange(2,2,10,10);
rg2.setFormulas(rg1A);
}