How to delete a chart using google script? - google-apps-script

I wrote a script to automatically generate charts from data on different tabs (after clicking a button on a new sheet).
I would now like to add a "reset" button which will automatically delete or remove the generated charts (so I can generate different charts with another button).
Can anyone help on how I can do this? I've tried the below, but to no avail....
function Finance_clear() {
function deleteAllChartsFromSheet(sheetName) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var chartsSheet = ss.getSheetByName(sheetName);
var n = chartsSheet.getCharts().length;
for (var i = n-1; i >= 0; i--) {
var chart = chartsSheet.getCharts()[i];
chartsSheet.removeChart(chart);
}
}
};

Delete All Charts from a Sheet
function delAllChartsFromSheet(name) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(name);
var chts=sh.getCharts();
for(var i=0;i<chts.length;i++){
sh.removeChart(chts[i]);
}
}
Sheet Reference

The code worked for me, I rewrote it as:
function remove_charts_from_sheet(sheet_name) {
const tab = SpreadsheetApp.getActive().getSheetByName(sheet_name);
const charts = tab.getCharts();
for(let i = 0; i < charts.length; i++){
tab.removeChart(charts[i]);
}
}

Related

Google Spreadsheet: loop through sheets stop working

i've a function called when opening my spreadsheet that aim to loop trought all sheets, activate them and set cursor to the last modified cell (saved by another function) and, a the last re-select the last used sheet (saved as before by another function).
It worked fine for years, but suddenly stopped working.
Here is my code:
function onOpen()
{
var timer = 2000;
var proprieta = PropertiesService.getScriptProperties();
var lastSheet = proprieta.getProperty("ultimo");
var lastModifiedCell = proprieta.getProperty(lastSheet);
var applicazione = SpreadsheetApp.getActive();
var fogli = applicazione.getSheets();
var ui = SpreadsheetApp.getUi();
for (var i=0; i < fogli.length; i++)
{
var attuale = fogli[i].getName();
var ultimorange = proprieta.getProperty(attuale);
applicazione.getSheetByName(attuale).getRange(ultimorange).activate();
//ui.alert(attuale);
//ui.alert(ultimorange);
Utilities.sleep(timer);
}
applicazione.getSheetByName(lastSheet).getRange(lastModifiedCell).activate();
}
Doesn't return any error but the for loop is not selecting anymore sheet and range given.
If i enable the ui.alert the sheet name and range are displayed correctly.
The last selection ath the end, works correctly...i have no idea why it happens...
Is there anyone who can help me figure out what's wrong?
Thanks to anyone who will answer!!!
I was able to replicate the issue. To prevent this from happening, you can use SpreadsheetApp.flush()
Your code:
function onOpen()
{
var timer = 2000;
var proprieta = PropertiesService.getScriptProperties();
var lastSheet = proprieta.getProperty("ultimo");
var lastModifiedCell = proprieta.getProperty(lastSheet);
var applicazione = SpreadsheetApp.getActive();
var fogli = applicazione.getSheets();
var ui = SpreadsheetApp.getUi();
for (var i=0; i < fogli.length; i++)
{
var attuale = fogli[i].getName();
var ultimorange = proprieta.getProperty(attuale);
var range = applicazione.getSheetByName(attuale).getRange(ultimorange).activate();
SpreadsheetApp.flush();
Utilities.sleep(timer);
}
applicazione.getSheetByName(lastSheet).getRange(lastModifiedCell).activate();
ui.alert("Activation Done");
}
I'm new but i found something recently (i come from excel scripts) :
getting a "range" and getting it's a1Notation are totally different.
Maybe your "ultimorange" (from "attuale") is a range and NOT the notation of the range. It would make getRange(ultimorange) not working.

Google forms duplicates sections when running Google Apps Script multiple times

I've got a code in Google Apps Script to automatically populate a Google Form. It works perfectly, but there's one big problem. When I run the script multiple times, the Forms copies itself. So, instead of overwriting the previous run, it adds a whole new (identical) Form. For example: after running the code one time, it shows 4 sections. But when I run it two times, it shows 8 sections, and so on.
Does anybody know how to solve this?
Here's the used code:
function rosterMaker() {
//spreadsheet id of the rosters
var SHEET_ID = FormApp.getActiveForm().getDestinationId();
var ss = SpreadsheetApp.openById(SHEET_ID)
var form = FormApp.getActiveForm();
var sheets = ss.getSheets().filter(function(sheet) {return sheet.getName().match(/Roster/gi);});
//add multiple choice item
var classSelect = form.addMultipleChoiceItem();
classSelect.setTitle('Choose a class');
var classChoices = [];
for(var i = 0; i < sheets.length; i++) {
var className = sheets[i].getName();
var classSection = form.addPageBreakItem()
.setTitle(className)
.setGoToPage(FormApp.PageNavigationType.SUBMIT);
var students = getStudents(sheets[i]);
var studentSelect = form.addCheckboxItem()
.setTitle(className + 'absent')
.setHelpText('Select the students who are absent from this class');
var studentChoices = [];
for(var j = 0; j < students.length; j++) {
studentChoices.push(studentSelect.createChoice(students[j]));
}
studentSelect.setChoices(studentChoices);
classChoices.push(classSelect.createChoice(className, classSection));
}
classSelect.setChoices(classChoices);
}
function getStudents(sheet) {
var studentValues = sheet.getDataRange().getValues();
var students = [];
for(var i = 1; i < studentValues.length; i++) {
students.push(studentValues[i].join(' '));
}
return students;
}
What you need to do is remove all the items you populated in your form before populating it again. Best place to add it on should be before adding anything to the form.
Based on your script, you can add it after var form = FormApp.getActiveForm();
See my sample code below on how it works.
var formID = <FORM_ID>;
var fData = FormApp.openById(formID);
function clearForm(){
// function that clears all items in your form
var items = fData.getItems();
// always delete while there is remaining item
while(items.length > 0){
fData.deleteItem(items.pop());
}
}
function populateForm() {
// call clearForm to prevent duplicating populated questions
clearForm();
// section to populate your form
// ....
}
If you have any issues/problems on making the code work, feel free to comment below.

How to automatically merge cells in Google Sheets upon creation of new Sheet

I am trying to figure out a way to make Google Sheets automatically merge Cells A1-C1 when a new sheet is created. My coworker and I have been trying to figure out the script that would make this happen, but everything we have tried only changes the previous Sheet we were working on, not the new one.
So far these are the two scripts we have tried, just to get some sort of result we are looking for:
function formatCells() {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var s = ss.getSheetByName('Combined')
var range = s.getDataRange()
var values = range.getValues();
for( var row = values.length -1; row >= 0; --row)
if (values[row][1] == 'Hello')
{s.getRange(row+1,1).mergeAcross();
}
}
and
function newSheetTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('newSheet')
.forSpreadsheet(ss)
.onChange()
.create();
}
function newSheet(e){
if (e.changeType == 'INSERT_GRID') {
SpreadsheetApp.flush();
SpreadsheetApp.getActiveSheet().getRange('A1:C1').merge();
}
}
Does anyone have an idea of where we went wrong?
The problem is that theonChange trigger is not able to detect the active sheet correctly
Retrieving the active sheet on trigger will always return you the first sheet, as you can easily verify with
function myFunction(e) {
Logger.log(e.changeType);
if(e.changeType=="INSERT_GRID"){
Logger.log(SpreadsheetApp.getActive().getActiveSheet().getName());
}
}
So you need to implement a workaround.
For example:
Strore the present sheet names in Script properties
When the trigger fires and the condition e.changeType=="INSERT_GRID" is fullfilled:
Compare the currently present sheet number to the one stored in script properties to evaluate either a new sheet has been inserted
If the sheet number increased - find the name of the new sheet with indexOf()
Merge cells on the new sheet and update the script properties
Code snippet:
var ss = SpreadsheetApp.getActive();
//run me once
function firstSetUp(){
var sheets = ss.getSheets();
var names = [];
for (var i = 0; i < sheets.length; i++){
names.push(sheets[i].getName())
}
PropertiesService.getScriptProperties().setProperty("sheets", JSON.stringify(names) );
}
//run me on trigger
function newSheet(e) {
if(e.changeType=="INSERT_GRID"){
var newSheets = ss.getSheets();
var oldSheetNames = JSON.parse(PropertiesService.getScriptProperties().getProperty("sheets"));
Logger.log(oldSheetNames);
var length = oldSheetNames.length;
Logger.log("length : " + length);
if (length != newSheets.length){
for (var i = 0; i < newSheets.length; i++){
if(oldSheetNames.indexOf(newSheets[i].getName()) == -1){
var newSheet = newSheets[i];
Logger.log(newSheet.getName());
newSheet.getRange('A1:C1').merge();
oldSheetNames.push(newSheet.getName());
PropertiesService.getScriptProperties().setProperty("sheets", JSON.stringify(oldSheetNames));
break;
}
}
}
}
}

Why does ss.deleteSheet() unhide one hidden sheet

I've written a little script that automatically deletes all Sheets my user accidentally create.
Often, when accessing the spreadsheet with their phone, they accidentally click on "+" and create tons of empty sheets, like Sheet101, Sheet102, etc.
My script is very simple: gets all sheets, and if the name starts with Sheet, just deletes it.
It works perfectly, but after deleting all the undesired sheets, the problem is that it automatically unhides the first hidden sheet (some sheets are hidden for practical use, like they contain lists).
For example, if I have the following hidden sheets:
oldform, list1, list2, ...
After executing the script, the sheet oldform will be unhidden, thus appearing to the users.
Here is two codes I've tried:
The simple one:
function DELETESHEETS2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (i = 0; i < sheets.length; i++) {
if ( sheets[i].getName().indexOf("Sheet") > -1 ) {
ss.deleteSheet(sheets[i]);
}
}
}
The one I tried to modify to solve my problem:
function DELETESHEETS3() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (i = 0; i < sheets.length; i++) {
if ( sheets[i].getName().indexOf("Sheet") > -1 ) {
label = sheets[i].getName();
toto = ss.getSheetByName(label);
ss.deleteSheet(toto);
}
}
}
The reverse loop
With this one, the hidden sheet appears at the beginning and not at the end:
function DELETESHEETS2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (i = sheets.length -1; i > 0 ; i--) {
if ( sheets[i].getName().indexOf("Sheet") > -1 ) {
ss.deleteSheet(sheets[i]);
}
}
}
A partial solution
This one works, thanks to #OMila. But it does not explain the weird behavior of a simple loop. Maybe I missed something?
function DELETESHEETS2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var hdnShts = [];
for (var i = 0; i < sheets.length; i++) {
if(sheets[i].isSheetHidden()) {
hdnShts.push(sheets[i].getName()); //saving all the hidden sheet names for later
}
}
for (i = 0; i < sheets.length; i++) {
if ( sheets[i].getName().indexOf("Sheet") > -1 ) {
ss.deleteSheet(sheets[i]);
}
}
for(var i = 0; i<hdnShts.length; i++) {
ss.getSheetByName(hdnShts[i]).hideSheet(); //just to make sure all the hiddens remain hidden
}
}
Thanks for your help if you have any idea!
PS: this problem is easy to reproduce, just create lots of sheets including one with a name other than Sheetxxx
Try this:
function delSheetsKeepHidden() {
var ss = SpreadsheetApp.getActive();
var shts = ss.getSheets();
var hdnShts = [];
for (var i = 0; i < shts.length; i++) {
if(shts[i].isSheetHidden()) {
hdnShts.push(shts[i].getName()); //saving all the hidden sheet names for later
}
}
for(var i = 0; i < shts.length; i++) {
if(shts[i].getName().slice(0,5).toLowerCase() == "sheet") {
ss.deleteSheet(shts[i]); //I think this is just deleting the sheet and not the array element in shts so no reason to keep track of deleted sheets like when deleting rows in a spreadsheet
}
}
for(var i = 0; i<hdnShts.length; i++) {
ss.getSheetByName(hdnShts[i]).hideSheet(); //just to make sure all the hiddens remain hidden
}
}
I've been forcing myself to get away from old school loops.
function delSheetsKeepHidden() {
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
var hdnShts=[];
shts.forEach(function(sht){if(sht.isSheetHidden()){hdnShts.push(sht.getName())}});
shts.forEach(function(sht){if(sht.getName().slice(0,5).toLowerCase()=="sheet"){ss.deleteSheet(sht);}});//I think this is just deleting the sheet and not the array element in shts so no reason to keep track of deleted sheets like when deleting rows in a spreadsheet
hdnShts.forEach(function(name){ss.getSheetByName(name).hideSheet();});//just to make sure all the hiddens remain hidden
}

Need help creating For loop in Script editor to make Script run code on all sheets in a Google Spreadsheet

I have a script that I am trying to run on all sheets within a google doc and dont know how to make that work. IT works for one sheet, but I have numerous sheets all with the same format that need to be checked. Thanks.
Here's the script I am trying to apply to all sheets.
function sendEmail(email_address, email_subject, email_message) {
MailApp.sendEmail(email_address, email_subject, email_message);
}
function timestamp() {
return new Date()
}
var EMAIL_SENT = "EMAIL_SENT";
function test_sendEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.setActiveCell('A2');
var criterion_cutoff = 5;
var i = 0;
var startRow = 2;
var addr;
var subj;
var msg;
var timecheck
do {
addr = cell.offset(i,0).getValue();
subj = cell.offset(i,1).getValue();
msg = cell.offset(i,2).getValue();
criterion = cell.offset(i,3).getValue();
timecheck = cell.offset(i,11).getValue();
if (timecheck > 0) {
if(criterion < criterion_cutoff) {
sendEmail(addr,subj,msg);
Browser.msgBox('Sending email to: ' + addr);
}}
i++;
} while( cell.offset(i, 0).getValue().length > 0 )
sheet.getRange(2, 5).setValue(timestamp());
Browser.msgBox('Done!');
}
You can use an array of IDs that you want to apply the script to by making the following changes:
1. Change
function test_sendEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
to:
function test_sendEmail(sheetID) {
var sheet = SpreadsheetApp.openById(sheetID);
2 . Define an array of IDs of sheets that you want to change:
var idArray = [Spreadsheet1_id, Spreadsheet2_id, ...];
3 . create a loop that would cycle through all the elements of the array
for(var k in idArray) test_sendEmail(idArray[i]);
If you don't want to manually populate the idArray, you can create a function that would pull IDs of all spreadsheets in your Google Drive. If that is what you want, then use the following code instead of the line of code I provided you with in point 2.
var sprArr = DocsList.getFilesByType("spreadsheet");
var idArray;
for(var k in sprArr) idArray.push(sprArr[i].getId());