Reading and updating multiple tabs in the same spreadsheet - javascript

I have a Google Spreadsheet where I retrieve data using the Google Analytics addon. In a tab I combine all the data and use this specific tab for my Google Datastudio report. In here I analyze my Google Ads campaign data - the costs and the conversions.
This Spreadsheet has multiple tabs (each tab contains a specific conversion) I need to be able to read and write for multiple tabs. It has to read the Google Analytics data and write this data in another sheet.
I originally created this script to read from 1 tab and to write to 1 tab and it was working. Then I noticed I will need to do this for almost all the tabs.
This is my current script:
function readAndWrite() {
var spreadsheetId = 'xxx';
var counter = 0;
var rangeName = ['readingTab1!A16:C','readingTab2!A16:C','readingTab3!A16:C','readingTab4!A16:C','readingTab5!A16:C'];
var rangePush = ['writingTab1','writingTab2','writingTab3','writingTab4','writingTab5','writingTab5'];
var values = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeName[counter]).values;
var data = [];
if (!values) {
Logger.log('No data found.');
} else {
Logger.log('Starting script.');
Logger.log('There are ' + values.length + ' unique rows.');
Logger.log(values[0][2]);
while (counter < 5){
data.length = 0;
for (var row = 0; row < values.length; row++) {
var campaign = values[row][0];
var date = values[row][1];
var cost = values[row][2];
data.push({range: rangePush[counter], values: [[date, campaign, cost]]});
}
counter++;
Sheets.Spreadsheets.Values.batchUpdate({data: data, valueInputOption: "USER_ENTERED"},spreadsheetId);
}
}
Logger.log('Finished.');
}
In the rangeName I have created an array with the names and ranges of my tab where it should read from.
In the rangePush I created an array with the names where it should push the data to.
When I run the script I receive the following error:
GoogleJsonResponseException: API call to sheets.spreadsheets.values.batchUpdate failed with error: Invalid data[1886]: Unable to parse range: writingTab2.
Is there anyone who can see what is going wrong and able to help me out here?
I hope my explanation is clear, if not please let me know.
Thanks!

I believe your goal is as follows.
You want to copy the values from readingTab#!A16:C sheet to writingTab# sheet using Sheets API with Google Apps Script.
Those sheet names are put to the arrays and each index of both is corresponding to the copy and paste sheets.
If my understanding is correct, how about the following modification?
At first, about your error, when I saw your sample Spreadsheet, there are 3 read and write sheets of 'readingTab1!A16:C', 'readingTab2!A16:C', 'readingTab3!A16:C' and 'writingTab1', 'writingTab2', 'writingTab3'. But your script uses while (counter < 5){. By this, the range becomes undefined after counter is more than 4. I thought that this might be the reason of your issue.
In order to achieve your goal by removing this issue, how about the following modified script?
Modified script:
When spreadsheets.values.batchGet and spreadsheets.values.batchUpdate methods are used, the script becomes the following simple script.
function readAndWrite() {
var spreadsheetId = 'xxx';
var rangeName = ['readingTab1!A16:C', 'readingTab2!A16:C', 'readingTab3!A16:C'];
var rangePush = ['writingTab1', 'writingTab2', 'writingTab3'];
var values = Sheets.Spreadsheets.Values.batchGet(spreadsheetId, {ranges: rangeName}).valueRanges;
values.forEach((v, i) => v.range = rangePush[i]);
Sheets.Spreadsheets.Values.batchUpdate({ data: values, valueInputOption: "USER_ENTERED" }, spreadsheetId);
}
In this modified script, it supposes that there are 3 read and write sheets of 'readingTab1!A16:C', 'readingTab2!A16:C', 'readingTab3!A16:C' and 'writingTab1', 'writingTab2', 'writingTab3'. When you add more sheets, please add them to the arrays of rangeName and rangePush.
Note:
In this script, please check the values of rangeName and rangePush and the sheet names of your Spreadsheet again.
This script uses Sheets API. So, please enable Sheets API at Advanced Google services.
I think that in your situation, the Spreadsheet service instead of Sheets API can also achieve your goal. But I think that the process cost will be lower when Sheets API is used. But, when you want to achieve your goal without using Sheets API, you can also use the following script.
function readAndWrite2() {
var spreadsheetId = 'xxx';
var rangeName = ['readingTab1!A16:C', 'readingTab2!A16:C', 'readingTab3!A16:C'];
var rangePush = ['writingTab1', 'writingTab2', 'writingTab3'];
var ss = SpreadsheetApp.openById(spreadsheetId);
rangeName.forEach((r, i) => ss.getRange(r).copyTo(ss.getSheetByName(rangePush[i]).getRange("A1")));
}
References:
Method: spreadsheets.values.batchGet
Method: spreadsheets.values.batchUpdate

Related

is there a way I can update multiple non-contiguous google sheet ranges using apps script all at the same time? (using SpreadsheetApp not sheets api)

lets assume we have a table of 4 x 4 starting from A1, now normally if I want to update it's values at once I'll just do
sheet.getRange("A1:D4").setValues(values);
now if the data is not contiguous so I have 3 4 x 4 separate tables with other data between them but I have them named as named ranged i.e (table1, table2, table3), can I do sth like:
sheet.getNamedRanges([table1range, table2range, table3range]).setValues([table1data, table2data, table3data]);
instead of:
sheet.getRange(table1range).setValues(table1data);
sheet.getRange(table2range).setValues(table2data);
sheet.getRange(table3range).setValues(table3data);
so that now all the 3 tables will be updated in the same operation instead of 3 different operations to save execution time?
thank you in advance.
I believe your goal is as follows.
You want to achieve the script like sheet.getNamedRanges([table1range, table2range, table3range]).setValues([table1data, table2data, table3data]); in order to reduce the process cost.
Namely, you want to put the values for each range to Google Spreadsheet using Google Apps Script. And, the range is
In this case, I think that Sheets API can be used for achieving your goal as follows. In this case, this thread might be useful. Ref When Sheets API is used for your situation, the process cost can be reduced rather than that of the method for putting the values to each range in the loop using Spreadsheet Service (SpreadsheetApp). Ref
const spreadsheetId = "###"; // Please set the Spreadsheet ID.
const ranges = [table1range, table2range, table3range]; // Please set table1range, table2range, table3range as A1Notation.
const values = [table1data, table2data, table3data]; // Please set table1data, table2data, table3data.
const data = ranges.map((e, i) => ({range: e, values: [[values[i]]]}));
Sheets.Spreadsheets.Values.batchUpdate({data, valueInputOption: "USER_ENTERED"}, spreadsheetId);
But, in this case, I thought that it might be difficult a little to create the request body. So I created a Google Apps Script library as a wrapper for using Sheets API. When this library is used, the sample script is as follows.
1. Install library.
You can see the method for installing the library at here. And, this script uses Sheets API. So please enable Sheets API at Advanced Google services.
2. Sample script:
const ranges = [table1range, table2range, table3range]; // Please set table1range, table2range, table3range as A1Notation.
const values = [table1data, table2data, table3data]; // Please set table1data, table2data, table3data.
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
RangeListApp.getSpreadsheet(spreadsheet).getRangeList(ranges).setValues(values);
When I saw your script, I thought that this sample script might be in the same direction you expect.
When this script is run, each value of values is put to each range of ranges using Sheets API.
References:
Benchmark: Reading and Writing Spreadsheet using Google Apps Script
Method: spreadsheets.values.batchUpdate
RangeListApp
According to Tanaike's proposal, I suggest a slight modification
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheet = ss.getActiveSheet()
const ranges = [table1range2D, table2range2D, table3range2D];
const values = [data1values2D, data2values2D, data3values2D];
const data = ranges.map((e, i) => ({ range: `'${sheet.getName()}'!${e}`, values : values[i] }));
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, ss.getId());
for instance
function updateGoogleSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheet = ss.getActiveSheet()
const otherSheet = ss.getSheetByName('other')
const table1range2D = 'A1:B2', table2range2D = 'C4:D5', table3range2D = 'E7:F8'
const data1values2D = otherSheet.getRange('A1:B2').getValues(), data2values2D = otherSheet.getRange('C4:D5').getValues(), data3values2D = otherSheet.getRange('E7:F8').getValues()
const ranges = [table1range2D, table2range2D, table3range2D];
const values = [data1values2D, data2values2D, data3values2D];
const data = ranges.map((e, i) => ({ range: `'${sheet.getName()}'!${e}`, values : values[i] }));
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, ss.getId());
}
where in sheet named 'other' we can find in A1 =sequence(10,10,0,1)

Send value to Google Sheets via web app with formula in variable

I'm using a web app to send data to Google Sheets via Apps Script. I'm using 'appendrow' to post several variables. Everything works fine but one variable is a number, call it 'Amount'. I found out that I can type a Google Sheets type formula in my web app such as '=10-5', '=10*2', '=10/2' and when it gets posted to my Google Sheets it provides the expected result. Good! However, if I type '=10+5', I get an error in my Google Sheets. When I look at the formula bar, it shows '10 5'. Why does it not show the formula as '=10+5'? The previous examples that I gave all show the expected formula in the formula bar.
function doGet(e) {
// I cannot take credit for this script. I copied and modified it from another user.
var ss = SpreadsheetApp.openById("FILE ID GOES HERE")
var sh = ss.getSheetByName("Register"); //sheet name is 'Register'
var date = e.parameter.date;
e.parameter.date = '1/2/2023'
var amount = e.parameter.amount;
var paytype = e.parameter.paytype;
var payid = e.parameter.payid;
var maincat = e.parameter.maincat;
var subcat = e.parameter.subcat;
var explanation = e.parameter.explanation;
sh.appendRow([date, amount, paytype, payid, maincat, subcat,
explanation]);
}
So when data get passed to Google Sheets via the sh.appendRow, the amount always works correctly when it contains, for example:
'10.52', '=10-5', '=10*2', '=10/2', but not '=10+5'. Instead, I get '10 5' and shows an error in the spreadsheet cell.
You may have guessed that I am a novice when it comes to script.
Thank you for any ideas.

manipulate the table locally, i.e. read the table into memory, process and then write to server

I have a script that steps through a sheet, then updates the sheet content and adds calendar items. I noticed that the execution takes up to 3 minutes and I suspect that the server calls are the reason. This is how I access the data today.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var startRow = 2; // First row of data to process
Now I wanted to make it better and manipulate the table locally, i.e. read the table into memory, process and then write to server in one go. I am sure there are plenty similar issues on SO for google apps script, if I only could get a good search term. "async bulk request" did not give me good hits on SO. Any hints on available issues or how to search for them?
Then I can start with selfstudy.
This particular best practice is described in Google's documentation here
According to the docs the correct way to read data from a spreadsheet, manipulate it, and then save it again is as follows:
function readAndWriteInBulk() {
// read all the data in the active sheet at once with getValues()
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getDataRange();
const data = range.getValues();
// manipulate the data as you like
for (const row of data) {
// do something to a row
Logger.log(row);
}
// paste the data at once in a new sheet with setValues()
const newSheet = SpreadsheetApp.insertSheet();
const pasteRange = newSheet.getRange(1, 1, data.length, data[0].length);
pasteRange.setValues(data);
}
References:
getValues()
setValues()

Script to delete a user account from G Suite Admin SDK

I am writing a script that fetches a data from a spread sheet and based on every entry entered in the spread sheet, it fetches the data which is basically email addresses and deletes a user's account from the domain.
//** Delete the users on submitting the "Submit" button
function onFormSubmit(e) {
//Logger.log(e);
//Run this function passing on event "e" when the form is submitted.
//Object e has form parameters sourced from the sheet attached to the form
deleteUsers(e);
}
//Logs all the info from the spreadsheet and deletes the user
function deleteUsers() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
Logger.log('managerEmail: ' + data[i][0]);
Logger.log('email: ' + data[i][1]);
Logger.log('trasferToEmail: ' + data[i][3]);
var managerEmail = data[i][0];
var email = data[i][1];
var trasferToEmail = data[i][3];
var request = {
'url': 'https://www.googleapis.com/admin/directory/v1/users/' + email,
'method' : 'DELETE'
};
}
But I am still unsuccessful in deleting an account. I actually tried to implement it based on this doc https://developers.google.com/admin-sdk/directory/v1/reference/users/delete but didn't know how to use it. Any ideas? Sorry if this is a stupid question! I am a novice in Google scripts.
You're sending e to deleteUsers(), but that function doesn't receive any parameters. No need to access the spreadsheet data when it's already provided by the onFormSubmit()–look at the event object documentation for reference.
function deleteUser(e) {
var data = e.namedValues;
var managerEmail = data["Manager Email"][0]; //You'll need to adjust the field names to match with your form data
var email = data["Email"][0];
var transferToEmail = data["Transfer to Email"][0];
var response = AdminDirectory.Users.remove(email);
}
To make sure that your trigger is correctly set up, first have the form responses get saved to your spreadsheet. Then Edit > Current project's triggers and copy these settings:
To make AdminDirectory work, you need to enable advanced services. (In the script editor, go to Resources > Advanced Google services and switch "on" Admin Directory API. Then click the link at the bottom of the modal to enable the Admin SDK in the API Console.)
If I was mistaken about what data the form is collecting and you really do need to pull the data from spreadsheet (assuming the form isn't connected to the sheet), then you need to create a trigger for when there is a submission to that form. Run this function to install the trigger.
function installFormTrigger() {
var form = FormApp.openById("FORM_ID");
ScriptApp.newTrigger("deleteUsers")
.forForm(form)
.onFormSubmit()
.create();
}
Then your original deleteUsers() function will work almost as you had it, but with the addition of AdminDirectory.
function deleteUsers() {
var sheet = SpreadsheetApp.getActive().getSheetByName("SheetName"); //HIGHLY recommend using this instead
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
var managerEmail = data[i][0];
var email = data[i][1];
var trasferToEmail = data[i][3];
var response = AdminDirectory.Users.remove(email);
}
}
Note that in your for loop, you might come across an invalid email or AdminDirectory could through an error, so I'd suggest implementing try...catch and logging.
It would be better to use AdminDirectory.Users.remove(email); rather than making a request to the API like you are doing.
Keep it in a variable if you want to log the response var request = AdminDirectory.Users.remove(data[i][1]);
To activate the AdminDirectory;
Go to Resources -> Advanced Google services
Enable the Admin Directory and then click on the link to the Google API console.
Click Enable APIs and Services
Search for Admin SDK
Click on Admin SDK and then click 'Enable'

Protect Row using username data on Google sheet

I am able to read a manipulate scripts but am unable to write my own effectively. I would like to ask for some help.
The Issue: I have a sheet where managers most go in and make their approval or rejection right from the sheet. Their email addresses are in the sheet as data.
Goal: I have been trying to find a script I can modify that will look at the column the user's emails at kept and use that data to set the permission for the row automatically instead of me having to create the protected ranges manually for 700+ managers.
I really appreciate any help on this.
The following is what little code I've been experimenting with. I don't know if it will help.
enter code herefunction initiate (){
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues()
for(n=0;n<values.length;++n){
var user = values[1][1] ; // x is the index of the column starting from 0
var manager = user.valueOf();
Logger.log(manager);
}
var protections = values.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
}

Categories