Deleting sheets not found in lookup - google-apps-script

I am trying to delete unneeded sheets from a template once relevant information has been copied across. To do this I am looking up the sheet name with a check list. If the lookup returns a value of 0.0 then I want to delete the sheet.
function myFunction() {
var studentsheet = SpreadsheetApp.openById('1Qj9T002nF6SbJRq-iINL2NisU7Ld0kSrQUkPEa6l31Q').;
var sheetsCount = studentsheet.getNumSheets();
var sheets = studentsheet.getSheets();
for (var i = 0; i < sheetsCount; i++){
var sheet = sheets[i];
var sheetName = sheet.getName();
Logger.log(sheetName);
var index = match(sheetName);
Logger.log(index);
if (index = "0.0"){
var ss = studentsheet.getSheetByName(sheetName).activate();
ss.deleteactivesheet();
}
else {}
}
function match(subject) {
var sourcesheet = SpreadsheetApp.openById('14o3ZG9gQt9RL0iti5xJllifzNiLuNxWDwTRyo-x9STI').getSheetByName("Sheet6").activate();
var lookupvalue = subject;
var lookuprange = sourcesheet.getRange(2, 2, 14, 1).getValues().map(function(d){ return d[0] });
var index = lookuprange.indexOf(subject)+1;
return index;
}
};
The problem is at the end when trying to delete the sheet. I have amended the code so it selects the sheet and makes it active but in the next line I am not allowed to call .deleteactivesheet(). Does anyone know how I can write this end part where I can select the sheet based on the index score being 0 and then delete it?

To delete a Sheet from a Spreadsheet, there are two applicable Spreadsheet class methods (as always, spelling and capitalization matter in JavaScript):
Spreadsheet#deleteSheet, which requires a Sheet object as its argument
Spreadsheet#deleteActiveSheet, which takes no arguments
The former is suitable for any type of script, and any type of trigger, while the latter only makes sense from a bound script working from a UI-based invocation (either an edit/change trigger, menu click, or other manual execution), because "activating" a sheet is a nonsense operation for a Spreadsheet resource that is not open in a UI with an attached Apps Script instance.
The minimum necessary modification is thus:
var index = match(sheet);
if (index === 0) { // if Array#indexOf returned -1 (not found), `match` returned -1 + 1 --> 0
studentsheet.deleteSheet(sheet);
}
A more pertinent modification would be something like:
function deleteNotFoundSheets() {
const studentWb = SpreadsheetApp.openById("some id");
const lookupSource = getLookupRange_(); // assumes the range to search doesn't depend on the sheets that may be deleted.
studentWb.getSheets().filter(function (s) {
return canDelete_(lookupSource, s.getName());
}).forEach(function (sheetToDelete) {
studentWb.deleteSheet(sheetToDelete);
});
}
function getLookupRange_() {
const source = SpreadsheetApp.openById("some other id");
const sheet = source.getSheetByName("some existing sheet name");
const r = sheet.getRange(...);
return r.getValues().map(...);
}
function canDelete_(lookupRange, subject) {
/** your code that returns true if the subject (the sheet name) should be deleted */
}
This modification uses available Array class methods to simplify the logic of your code (by removing iterators whose only purpose is to iterate, and instead expose the contained values to the anonymous callback functions). Basically, this code is very easily understood as "of all the sheets, we want these ones (the filter), and we want to do the same thing to them (the forEach)"
Additional Reading:
JavaScript comparison operators and this (among others) SO question
Array#filter
Array#forEach

If just like me you have been struggling to find a working example of #tehhowch above solution, here it is:
function deleteSheetsWithCriteria() {
let ss = SpreadsheetApp.getActiveSpreadsheet(),
sheetList = ss.getSheetByName('List'),
list = sheetList.getRange('A1:A'+sheetList.getLastRow()),
lookupRange = list.getValues().map(function(d){ return d[0] }) + ',List'; // List of sheets NOT to delete + lookuprange sheet
Logger.log(lookupRange)
//Delete all sheets except lookupRange
ss.getSheets().filter(function (sheet) {
return deleteCriteria_(lookupRange, sheet.getName());
}).forEach(function (sheetToDelete) {
ss.deleteSheet(sheetToDelete);
});
}
function deleteCriteria_(lookupRange, sheet) {
var index = lookupRange.indexOf(sheet);
Logger.log(index)
if (index > 0) {0} else {return index}; // change line 19 to 'return index;' only, if you want to delete the sheets in the lookupRange, rember to remove the lookupRange in variable LookupRage
}

Related

How to get a total across all sheets for cells based on criteria from another cell?

I have a budget spreadsheet with tabs for every pay period. These tabs are created as needed and don't have names I can easily know in advance. For instance, one will be "10/15 - 10/28" because that's the pay period. Next month I create a new one with "10/29 - 11/11." I'd like to be able to sum a value across all sheets. For example, every sheet has a row named "Save," some sheets have a row named "Rent", but not every sheet will contain rows with those names and when they do they won't always be in the same cell number.
Sample sheet
I've seen some examples where there's a bunch of SUMIFs and every sheet is manually named but I'd much rather not have to do that because this sheet gets copied fairly often and the sheet names will never be the same.
=SUMIFS('Tab 1' !A1:A10, 'Tab 1'!B1:B10, "Rent")
+SUMIFS('Tab 2' !A1:A10, 'Tab 2'!B1:B10, "Rent")
+SUMIFS('Tab 3' !A1:A10, 'Tab 3'!B1:B10, "Rent")
Is this possible with either a standard formula or a script?
Sample Data
Desired final tab
Column 1's values are known in advance so those can be hardcoded. For instance, there will never be a random "yet more stuff" appear which I wouldn't sum up by adding a new row to the final tab.
While there's another answer that works for this, I think the use of text finders and getRange, getValue and setFormula in loops is not the best approach, since it greatly increases the amount of calls to the spreadsheet service, slowing down the script (see Minimize calls to other services).
Method 1. onEdit trigger:
An option would be to use an onEdit trigger to do the following whenever a user edits the spreadsheet:
Loop through all sheets (excluding Totals).
For each sheet, loop through all data.
For each row, check if the category has been found previously.
If it has not been found, add it (and the corresponding amount) to an array storing the totals (called items in the function below).
If it has been found, add the current amount to the previous total.
Write the resulting data to Totals.
It could be something like this (check inline comments for more details):
const TOTAL_SHEET_NAME = "Totals";
const FIRST_ROW = 4;
function onEdit(e) {
const ss = e.source;
const targetSheet = ss.getSheetByName(TOTAL_SHEET_NAME);
const sourceSheets = ss.getSheets().filter(sheet => sheet.getName() !== TOTAL_SHEET_NAME);
let items = [["Category", "Amount"]];
sourceSheets.forEach(sheet => { // Loop through all source sheets
const values = sheet.getRange(FIRST_ROW, 1, sheet.getLastRow()-FIRST_ROW+1, 2).getValues();
values.forEach(row => { // Loop through data in a sheet
const [category, amount] = row;
const item = items.find(item => item[0] === category); // Find category
if (!item) { // If category doesn't exist, create it
items.push([category, amount]);
} else { // If category exists, update the amount
item[1] += amount;
}
});
});
targetSheet.getRange(FIRST_ROW-1, 1, items.length, items[0].length).setValues(items);
}
Method 2. Custom function:
Another option would be to use an Apps Script Custom Function.
In this case, writing the data via setValues is not necessary, returning the results would be enough:
const TOTAL_SHEET_NAME = "Totals";
const FIRST_ROW = 4;
function CALCULATE_TOTALS() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sourceSheets = ss.getSheets().filter(sheet => sheet.getName() !== TOTAL_SHEET_NAME);
let items = [["Category", "Amount"]];
sourceSheets.forEach(sheet => { // Loop through all source sheets
const values = sheet.getRange(FIRST_ROW, 1, sheet.getLastRow()-FIRST_ROW+1, 2).getValues();
values.forEach(row => { // Loop through data in a sheet
const [category, amount] = row;
const item = items.find(item => item[0] === category); // Find category
if (!item) { // If category doesn't exist, create it
items.push([category, amount]);
} else { // If category exists, update the amount
item[1] += amount;
}
});
});
return items;
}
Once the script is saved, you can use this function the same you would use any sheets built-in function:
The problem with this approach is that the formula won't recalculate automatically when changing any of the source data. In order to do that, see the above method.
Method 3. onSelectionChange trigger:
From your comment:
I'd love to be able to trigger it when the totals sheet is opened but that doesn't appear to be possible
You can do this by using an onSelectionChange trigger in combination with PropertiesService.
The idea would be that, every time a user changes cell selection, the function should check whether current sheet is Totals and whether the previously active sheet is not Totals. If that's the case, this means the user just opened the Totals sheet, and the results should update.
It could be something like this:
function onSelectionChange(e) {
const range = e.range;
const sheet = range.getSheet();
const sheetName = sheet.getName();
const previousSheetName = PropertiesService.getUserProperties().getProperty("PREVIOUS_SHEET");
if (sheetName === TOTAL_SHEET_NAME && previousSheetName !== TOTAL_SHEET_NAME) {
updateTotals(e);
}
PropertiesService.getUserProperties().setProperty("PREVIOUS_SHEET", sheetName);
}
function updateTotals(e) {
const ss = e.source;
const targetSheet = ss.getSheetByName(TOTAL_SHEET_NAME);
const sourceSheets = ss.getSheets().filter(sheet => sheet.getName() !== TOTAL_SHEET_NAME);
let items = [["Category", "Amount"]];
sourceSheets.forEach(sheet => { // Loop through all source sheets
const values = sheet.getRange(FIRST_ROW, 1, sheet.getLastRow()-FIRST_ROW+1, 2).getValues();
values.forEach(row => { // Loop through data in a sheet
const [category, amount] = row;
const item = items.find(item => item[0] === category); // Find category
if (!item) { // If category doesn't exist, create it
items.push([category, amount]);
} else { // If category exists, update the amount
item[1] += amount;
}
});
});
targetSheet.getRange(FIRST_ROW-1, 1, items.length, items[0].length).setValues(items);
}
Note: Please notice that, in order for this trigger to work, you need to refresh the spreadsheet once the trigger is added and every time the spreadsheet is opened (ref).
Reference:
onEdit(e)
Custom Functions in Google Sheets
onSelectionChange(e)
I wrote 2 scripts:
budgetTotal which takes a budgetCategory parameter, for example "Rent", and loops through all the sheets in the file to sum up the amounts listed on each sheet for that category.
budgetCreation which looks at your Totals sheet and writes these budgetTotal formulas in for each category you have listed.
I ran into a challenge which was, as I added new sheets the formulas wouldn't be aware and update the totals. So, what I did was create a simple button that executes the budgetCreation script. This way, as you add new payroll weeks you just need to press the button and - voila! - the totals update.
There might be a better way to do this using onEdit or onChange triggers but this felt like a decent starting place.
Here's a copy of the sheet with the button in place.
const ws=SpreadsheetApp.getActiveSpreadsheet()
const ss=ws.getActiveSheet()
const totals=ws.getSheetByName("Totals")
function budgetCreation(){
var budgetStart = totals.createTextFinder("Category").findNext()
var budgetStartRow = budgetStart.getRow()+1
var budgetEndRow = ss.getRange(budgetStart.getA1Notation()).getDataRegion().getLastRow()
var budgetCategoies = budgetEndRow - budgetStartRow + 1
ss.getRange(budgetStartRow,2,budgetCategoies,1).clear()
for (i=0; i<budgetCategoies; i++){
var budCat = ss.getRange(budgetStartRow+i,1).getValue()
var budFormula = `=budgetTotal(\"${budCat}\")`
ss.getRange(budgetStartRow+i,2).setFormula(budFormula)
}
}
function budgetTotal(budgetCategory) {
var sheets = ws.getSheets()
var total = 0
for (i=0; i<sheets.length; i++){
if (sheets[i].getName() != totals.getName()){
var totalFinder = sheets[i].createTextFinder(budgetCategory).findNext()
if (totalFinder == null){
total = 0
} else {
var totalValueFinder = sheets[i].getRange(totalFinder.getRow(),totalFinder.getColumn()+1).getValue()
total += totalValueFinder
}
}
}
return total
}

Absolute reference with UDF & filtering data in google sheets script

Hi I am begineer to scripts, here is the code and googlesheet for reference
What i am getting
what i want to achieve
/**
*#param quest1 Question of the note
*#param quest1 Answer of the note
*#customfunction*/
function DMNOTE(quest1,ans1,quest2,ans2,quest3,ans3,) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var result = quest1+'-'+ans1+','+quest2+'-'+ans2+','+quest3+'-'+ans3;
return result;
}
I want to achieve absolute reference for "quest" parameter and i want it to loop for rest of the coulmns till column where i enter the function.Also under "Formula Required" column i have put formla for reference thats how i want my UDF to work.
Follwing up i need to filter "Non-solicit agreement" and keep only "No" under it and Copy & Paste all colums highlited in blue to update tab.
function toFilter (){
// filter and retain "no" in non-solicit agreement
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Worksheet");
ss.getRange(1,1,ss.getLastRow(),ss.getLastColumn());
var createfilter = SpreadsheetApp.newFilterCriteria().setHiddenValues("Yes").build();
ss.getFilter().setColumnFilterCriteria(8, createfilter);
}
Hope i make sense. Any help is appriciated
To work with any number of arguments, you can have a function like below in your script
function DMNOTE(...arg){
let result = ''
for(let i=0;i<arg.length;i++){
result += `${arg[i]}-${arg[i+1]},`
i++;
}
return result.substring(0, result.length - 1);
}
And then form your spreadsheet you can call as =DMNOTE(A$1,A2,B$1,B2,C$1,C2) or =DMNOTE(A$1,A2,B$1,B2,C$1,C2,D$1,D2) The function will process all the arguments being passed and returns the result.
How to filter "non-solicit agreement" and take only data which has "no"
The code you provided comes already very close to what you desire, you just need to make the following adjustments:
setHiddenValues() expect an array, no a string, so change "Yes" to ["Yes"]
getFilter() only works to modify a filter that is present in the sheet already, in case there is none, it is better to delete the old filter and create a new one:
function toFilter(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Worksheet");
ss.getRange(1,1,ss.getLastRow(),ss.getLastColumn());
var createfilter = SpreadsheetApp.newFilterCriteria().setHiddenValues(["Yes"]).build();
if(ss.getFilter() != null) {
ss.getFilter().remove();
}
ss.getDataRange().createFilter().setColumnFilterCriteria(8, createfilter);
}
How to Copy & Paste all columns highlited in blue to update tab
Build a function that
calls toFilter()
checks for all rows either they are hidden
copies the non- hidden rows into an array
pastes this array to the sheet Update
function copyIfNotHidden(){
toFilter();
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = spreadsheet.getSheetByName("Worksheet");
var sheet2 = spreadsheet.getSheetByName("Update");
var data = sheet1.getDataRange().getValues();
var array = [];
for (var i = 0; i < data.length; i++){
if(sheet1.isRowHiddenByFilter(i+1)==false){
array.push(data[i]);
}
}
sheet2.getRange(sheet2.getLastRow()+1, 1, array.length, array[0].length).setValues(array);
}

Update/edit google sheets row values from html input

I have an html form where the client's data is inserted in and it appends row with the values on to a google sheet.
In the form, there's a field that searches and returns the clients data when searching for a specific value (id number).
function getID(IDsearch){
var ws = SpreadsheetApp.getActiveSheet();
var data = ws.getRange(3, 1, ws.getLastRow(), 36).getValues();
var dataInput = data.map(function(r){return r[7];});
var position = dataInput.indexOf(IDsearch);
var dataArray = ws.getRange(position+3, 1, 1, 36).getValues();
if(position > -1){
return dataArray;
} else {
return position;
}
}
After this runs, all the input fields in the form are populated with the data from that row.
I need to edit the values in the form and when submit it should overwrite/update the existing row with that id number.
In google sheets documentation, I've found the spreadsheets.values.update method, but I cannot figure this out. I'm pretty new in this and any help would be appreciated.
Thanks everyone!
You want to achieve the following flow.
Input "ID" to id="insertID" and click "Search by ID".
Show the values from Spreadsheet by searching "ID".
Edit the values of id="name" and id="ID".
When "Save data" is clicked, you want to update the values on the Spreadsheet.
From your replying, shared Spreadsheet and script, I could understand like above. If my understanding is correct, how about ths following modification? Please think of this as just one of several possible answers.
Modification points:
In your case, processForm at Google Apps Script side is required to be modified.
Search the row using formObject and overwrite the values of cells.
Modified script:
When your script is modified, please modify processForm at Google Apps Script side as follows. I remove the Spreadsheet ID from the URL. So please set it, before you test the script.
function processForm(formObject) {
var url = "https://docs.google.com/spreadsheets/d/###/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Database");
// I added and modified below script.
var ranges = ws.getRange(4, 2, ws.getLastRow() - 3, 1).createTextFinder(formObject.ID).findAll();
if (ranges.length > 0) {
for (var i = 0; i < ranges.length; i++) {
ranges[i].offset(0, -1, 1, 2).setValues([[formObject.name, formObject.ID]]);
}
} else {
ws.appendRow([formObject.name, formObject.ID]);
}
}
In this modification, when the same IDs are existing, all rows of the same IDs are overwritten. For example, if you want to modify the 1st one, please modify to ranges[0].offset(0, -1, 1, 2).setValues([[formObject.name, formObject.ID]]);.
Reference:
Class TextFinder
Try this:
function getID(IDsearch){
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();//dont know what the sheet is
var rg=sh.getRange(3,1,sh.getLastRow()-2,36);
var data=rg.getValues();
var idA=sh.getRange(3,8,sh.getLastRow()-2,1).getValues().map(function(r){return r[0];});//it looked like column 8 was your id column
var idx=idA.indexOf(IDsearch);
if(idx>-1) {
return ws.getRange(pos + 3,1,1,36).getValues()[0];//flattened the row to a 1d array
}else{
return idx;
}
}
#dianadfonseca, as #Tanaike points out, without more detail about your data structure, people will be speculating in order to answer your question. As I will be...
Please read the following answer, and tailor it to your needs if it works for you.
Example:
function getRow(id){
var ws = SpreadsheetApp.getActiveSheet();
// Number of headers to skip
var numHeaders = 2;
// the starting row
var startRow = numHeaders + 1;
// The column where the IDs are is known
var idCol = 8;
// The number of rows with data not headers
var numRows = ws.getDataRange().getLastRow() - numHeaders;
// An array with the ids to find a match in
// getRange() returns a 2D array, so you can transpose it to flatten it
var ids = ws.getRange(startRow,idCol,numRows).getValues();
ids = transpose(ids)[0];
// Get the index where id matches in ids
var row = ids.indexOf(id);
// If there's a match
if(row > -1){
// Correct row indexing
row = row + startRow;
}
return row;
}
function updateRow(row,data){
var ws = SpreadsheetApp.getActiveSheet();
// The column for each property is known
var propertyOneCol = 1;
// Update property using setValue()
ws.getRange(row,propertyOneCol).setValue(data.propertyOne);
// And so on...
}
// Transpose to avoid looping through the array
function transpose(a)
{
return Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
}
You can take a look the spreadsheet used for this example here with its bound script to play around.
Here is the function I used for testing
function test(){
// You are receiving this from your form
var data = {"propertyOne":"Juan","propertyTwo":20, "id":123467};
var id = data.id;
updateRow(getRow(id),data);
}

Google sparesheet formula/function to get random values without recalculating(randbetween)

I am trying to achive the following. In google sparesheet I have one sheet with values "AllValues", in another sheet "Randomvalues" I would like to get random values from sheet "AllValues".
I have tried two options, first I tried randbetween formula:
=INDEX(AllValues!A4:A103,RANDBETWEEN(1,COUNTA(AllValues!A4:A103)),1)
It is working, but it refresh/recalculate new values all the time column is changed. Googeled a lot and seems that there is not much to do to freeze already calculated results.
Next I tried function:
function random() {
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ss = sss.getSheetByName('Values'); //the sheet that has the data
var range = ss.getRange(1,1,ss.getLastRow(), 4); //the range you need: 4 columns on all row which are available
var data = range.getValues();
for(var i = 0; i < data.length; i++)
{
var j = Math.floor(Math.random()*(data[i].length)); //method of randomization
var element = data[i][j]; // The element which is randomizely choose
ss.getRange(i+1, 6).setValue(element);
}
}
But this function is not working for me, google sparesheet gives error on line 11, that setVaue is not allowed.
Line 11: ss.getRange(i+1, 6).setValue(element);
Googled this one too, there are lot of suggestion, but I am not very familiar with functions, I did not managed to get it working.
Hope that someone can help me out.
Using a formula assumes repeated calculations usually. You cannot prevent them and only can try to return old values instead. This task is not trivial, since any formula cannot refer to the same cell where the result is to be returned (a circular reference occurs). Do not use formulas for single time calculation.
On the other hand, using a script function makes it possible to generate required data directly and only once or on demand. I think, the function below will help you to understand all the neccesary steps for sample source and target ranges.
function random() {
var source = "AllValues!A4:A103",
target = "RandomValues!F2:F22";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceValues = ss.getRange(source).getValues(),
targetRange = ss.getRange(target),
targetValues = [];
while (targetValues.length < targetRange.getHeight()) {
var randomIndex = Math.floor(Math.random() * sourceValues.length);
targetValues.push(sourceValues[randomIndex]);
}
targetRange.setValues(targetValues);
}
You can run it manually or choose a proper trigger.
There are multiple ways of achieving this goal.
Custom Menu
As mentioned by #Tanaike, you can avoid the recalculation and the formula dependency by using a Custom Menu:
// #OnlyCurrentDoc
// Create a function that binds the "simple trigger" for the open event:
function onOpen(e) {
// Add a menu to the UI with the function we want to be able to invoke.
const ui = SpreadsheetApp.getUi();
ui.createMenu("Randomizer")
.addItem("Sample from 'AllValues' sheet", "sampleAllValues")
.addToUi();
}
You then need a function definition matching this name sampleAllValues, and when the user selects the associated menu option, it will be invoked with the permissions of the clicking user (the user will be prompted first to provide consent for access per the script's OAuth scopes).
function sampleAllValues() {
const wb = SpreadsheetApp.getActive();
const destination = wb.getSheetByName("RandomValues");
const source = wb.getSheetByName("AllValues");
if (!source || !destination)
throw new Error("Missing required sheets 'RandomValues' and 'AllValues'");
// Create a flat array of all non-empty values in all rows and columns of the source sheet.
const data = source.getDataRange().getValues().reduce(function (compiled, row) {
var vals = row.filter(function (val) { return val !== ""; });
if (vals.length)
Array.prototype.push.apply(compiled, vals);
return compiled;
}, []);
// Sample the smaller of 50 elements or 10% of the data, without replacement.
const sample = [];
var sampleSize = Math.min(50, Math.floor(data.length * .1));
while (sampleSize-- > 0)
{
var choice = Math.floor(Math.random() * data.length);
Array.prototype.push.apply(sample, data.splice(choice, 1));
}
// If we have any samples collected, write them to the destination sheet.
if (sample.length)
{
destination.getDataRange().clearContent();
// Write a 2D column array.
destination.getRange(1, 1, sample.length, 1)
.setValues(sample.map(function (element) { return [ element ]; }));
// Write a 2D row array
// destination.getRange(1, 1, 1, sample.length)
// .setValues( [sample] );
}
}
Custom Function
If you still wanted to use a custom function from the RandomValues sheet, e.g.
RandomValues!A1: =sampleAllValues(50, AllValues!A1:A)
then you would need to return sample instead of write to a specific sheet. Note that custom functions are treated deterministically--they are computed at the time of entry and then only recalculated when the values of their arguments change. Custom functions run with very limited scope, so be sure to review their restrictions.
The above usage hints that you might find it useful to allow passing in the number of desired samples, and the values to sample from:
function sampleAllValues(sampleSize, value2Darray) {
const data = value2Darray.reduce(function (compiled, row) {
/* as above */
}, []);
/* sample as above */
return sample; // Must be 2D row or 2D column array, or a single primitive e.g. `1`
}
No matter which route you take, be sure to review your script's error logging by viewing your script's Stackdriver logs. (View -> Stackdriver Logging)
References:
Sheet#getRange
Custom functions
Custom menus
Array#reduce
Array#map
Array#splice
.push.apply

Google forms script to delete responses counter

I have tried and combined a few pieces of script to delete rows, but this does not reset the counter. Help resetting responses would be appreciated.
My copy sheet function, and delete all rows function works, but the counter remains, showing 58 responses.
I use the triggers to set the copy and delete functions to occur daily. (sheet url excluding the "docs.google.com..." 0AvTM4SfinH2NdGp1MHdzWms2QnpUMnFiMHJXd1dlV1E&usp) This is what I have so far:
function CopySheet() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getSheets()[0];// here I chose to always get the first sheet in the spreadsheet
var inputRange = ss.getRange(1,1,ss.getLastRow(),7);
var data = inputRange.getValues();
var newData = [];
newData.push(['Timestamp','Full Name?','Email?','RAG']);
for(var n=1;n<data.length;++n){ // skip headers by starting at 1
for(var c=0;c<7;c=c+3){
var row = [];
if(c==0){row.push(data[n][0]) ; c++}else{row.push('')};
row.push(data[n][c])
row.push(data[n][c+1]);
row.push(data[n][c+1+1]);//Keep adding a new row and +1 for each extra column
newData.push(row);
}
}
//This next bit creates a copy of the sheet. I would rather a spreadsheet copy but could only get document copy to work
sh.insertSheet().getRange(1,1,newData.length,newData[0].length).setValues(newData);
var doc = DocumentApp.create('Responses document'); // create document
var file = DocsList.getFileById(doc.getId());
file.removeFromFolder(DocsList.getRootFolder());
file.addToFolder(DocsList.getFolder("Folder 1"));
var table = doc.getBody().appendTable(newData); // create table in a separate process so I can set the style below
var style = {};
style[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] = DocumentApp.HorizontalAlignment.CENTER; // this one has no effect
style[DocumentApp.Attribute.FONT_FAMILY] = DocumentApp.FontFamily.ARIAL;
style[DocumentApp.Attribute.FONT_SIZE] = 10;
style[DocumentApp.Attribute.FOREGROUND_COLOR] = '#0000ff';
style[DocumentApp.Attribute.BORDER_COLOR] = '#dddddd' ;
table.setAttributes(style);
}
//This section deletes the sheet, leaving the headers; "function deleteAllResponses()" at the bottom should reset counter but does not work
function DeleteSheet() {
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var datarange = sheet.getDataRange();
var lastrow = datarange.getLastRow();
var values = datarange.getValues();// get all data in a 2D array
for (i=lastrow;i>=2;i--) {
var tempdate = values[i-1][2]; // arrays are 0 indexed so row1 = values[0] and col3 = [2], If I add more columns I need to up this number
{
sheet.deleteRow(i);
function deleteAllResponses() {}
}
}
}
If you mean the counter responses shown on the form:
One option may be to use deleteAllResponses() (read carefully the documentation) from Class Form.
A minimal implementation can be:
/* CODE FOR DEMONSTRATION PURPOSES */
function deleteAllResponses() {
var form, urlForm = SpreadsheetApp.getActiveSpreadsheet().getFormUrl();
if (urlForm) {
form = FormApp.openByUrl(urlForm);
if (form) form.deleteAllResponses();
}
}
#user2847142, #brian-tompsett, #wchiquito
New Google Forms allows you to delete even individual responses from within a Google Form itself without the need of a script.
There is now a simpler method than the answer given by #wchiquito.
--This is now possible on the New Google Forms--
Google announcement on the 10th of February 2016. (googleappsupdates.blogspot.com/2016/02/new-google-forms-now-default-option.html)
How to delete ALL of the responses:
How to delete individual responses:
To delete individual responses you click on the "Responses" tab and choose "Individual". You locate the record you wish to delete and click on the trash can icon to delete that individual response.
This will also reset the counter.
However. The response/s will NOT be deleted from the connected to the form spreadsheet. You will have to manually delete those ones (or using a script).