Interactive Google Spreadsheet to Google Doc edit not working - google-apps-script

I have several projects and they respect the same structure they have a table of 8 rows and two columns and I wrote so far a script that takes all the docs and then puts them in a table with the information as column.It is a container bound script and I want to be used by several users.I am blocked for a quite long time because I want to make interactivity if I modify in the table it will modify in the Google Doc and vice-verso.I begin I tried with the column date if I modify the date and then click on the url of the Google Doc to see the change but it doesn't work.This is my code :
Edit this is my new modified code:
function modify_Date_Google_Spreadsheet_to_Google_Doc_Project(e) {
find_columns_in_projet();
Logger.log(">> The column URL >> " + COLUMN_URL );
Logger.log("The column date where we will modify it " + column_date_project);
var tss = SpreadsheetApp.openById(SPREADSHEET_ID);
var sheet = tss.getSheets()[0];
var numRows = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
//from the second line car the first line we have the headers
var data = sheet.getRange(1,1,numRows,lastColumn).getDisplayValues();
if ( ( e.range.getColumnIndex() == column_date_project ) )
{
var activeRow = SpreadsheetApp.getActiveRange().getRowIndex();
var URL = e.range.offset(0,1,1,1).getValue();
Logger.log('The URL is : ' + URL );
var body = DocumentApp.openByUrl(URL).getBody();
Logger.log('The body is ' + body );
if(body)
{
var ok = 0; //for the moment we don't have the table to modify the values we've put in the spreadsheet
var numChildren=body.getNumChildren();
var compteur=0;
//while we don't find the table we will search
while(ok ==0 && compteur<numChildren)
{
var child=body.getChild(compteur);
/** =========We are concerned by the first table with at least 8 rows ===**/
Logger.log('the type in the loop ' + child.getType());
//here is our table **/
if(child.getType()==DocumentApp.ElementType.TABLE && child.asTable().getNumRows() >= 8)
{
//so the variable gets 1 >> ok = 1
ok=1;
/** The number of rows in the Google Doc table **/
var numrows = child.asTable().getNumRows();
Logger.log('The number of rows is ' + numrows);
//Logger.log('The new date is ' + data[activeRow][colonne_date_de_projet-1]);
/** we will loop in the table **/
var k = 1; //we know the information is at right so we don't loop we will replace the value
/** is not working **********************************************/
//child.asTable().getCell(7, k).editAsText().setText( data[activeRow][column_date_project-1] ) ;
/**** is working ***/
child.asTable().getCell(7, k).editAsText().setText( 10 ) ;
}
compteur++; /** until we find our table **/
}
}
}
}
It's a trigger on edit because I have another one in the project This is the Google Spreadsheed with the script inside : https://docs.google.com/spreadsheets/d/1k_kj98U__0Bk44gh0qRFpaVx0ru3sN1pSPGiMQwimxo/edit?usp=sharing and my folder with the Google Doc projects is here : https://drive.google.com/drive/folders/1x1m7tqfoSY6yW5gvwoIoh9jRPuiqwADO?usp=sharing Any idea is great :) thank you very much in advance

I've tried another code that works only if I modify the date of the 4th project in this code I replaced only the
data_sheet[activeRow ][column_date_project-1] with
data_sheet[4][column_date_project-1] because activeRow is undefined
so any idea to recover the row of the active cell when I edit the data corresponding cell is great.
/** working with 4 as the 4 row's date modifies****/
function works(e) {
find_columns_in_projet();
Logger.log(">> The column URL >> " + COLUMN_URL );
Logger.log("The column date where we will modify it " + column_date_project);
var tss_bis = SpreadsheetApp.openById(SPREADSHEET_ID);
var sheet_bis = tss_bis.getSheets()[0];
var numRows_bis = sheet_bis.getLastRow();
var lastColumn_bis = sheet_bis.getLastColumn();
//from the second line car the first line we have the headers
var data_sheet = sheet_bis.getRange(1,1,numRows_bis,lastColumn_bis).getDisplayValues();
if ( ( e.range.getColumnIndex() == column_date_project ) )
{
var activeRow = SpreadsheetApp.getActiveRange().getRowIndex();
Logger.log('The active row is : ' + activeRow );
var URL = e.range.offset(0,1,1,1).getValue();
Logger.log('The URL is : ' + URL );
var body = DocumentApp.openByUrl(URL).getBody();
Logger.log('The body is ' + body );
if(body)
{
var ok = 0; //for the moment we don't have the table to modify the values we've put in the spreadsheet
var numChildren=body.getNumChildren();
var compteur=0;
//while we don't find the table we will search
while(ok ==0 && compteur<numChildren)
{
var child=body.getChild(compteur);
/** =========We are concerned by the first table with at least 8 rows ===**/
Logger.log('the type in the loop ' + child.getType());
//here is our table **/
if(child.getType()==DocumentApp.ElementType.TABLE && child.asTable().getNumRows() >= 8)
{
//so the variable gets 1 >> ok = 1
ok=1;
/** The number of rows in the Google Doc table **/
var numrows = child.asTable().getNumRows();
Logger.log('The number of rows is ' + numrows);
Logger.log('The new date is ' + data_sheet[4][column_date_project-1]);
/** we will loop in the table **/
var k = 1; //we know the information is at right so we don't loop we will replace the value
/** is working **********************************************/
child.asTable().getCell(7, k).editAsText().setText( data_sheet[4][column_date_project-1] ) ;
}
compteur++; /** until we find our table **/
}
}
}
}

Related

Create a Unique Sequential ID Generator - Google Scripts

I am attempting to find a way to create a google scripts function that will create unique sequential ID generator after looking through my google sheets document verifying that it will be a unique ID. I found this answer https://stackoverflow.com/a/58515330/14143011 which is exactly what I need however I do not want the ID to be inserted into the google sheet automatically, as I want to be able to use the ID in another function I am working on. When it comes to the date I am using this google scripts function to retrieve the current date:
const timezone = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
function datestamp() {
var datestamp_format = "yyyy-MM-dd";
return Utilities.formatDate(new Date(), timezone, datestamp_format);
}
To clarify what I am attempting to do, I have this function which creates folders for files I am uploading:
function createSubfolder(){
var dropbox = Utilities.formatDate(new Date(), "US/Eastern", "_yyyy-MM-dd");
I want to change the name to also contain the generated ID. I want the 'createSubfolder' function to call the function that generates the ID and return it to a variable such as this:
function createSubfolder(){
var generatedID = idGeneratorFunction()
var dropbox = generatedID + Utilities.formatDate(new Date(), "US/Eastern", "_yyyy-MM-dd");
Thank you!
The solution writes the ID using this statement
sheet.getRange(2, indexId + 1, newId.length).setValues(newId);
Just comment it out and change the customer function to return the ID and then you can use it however you like.
Recommended solution:
Instead of setting the newID value into the sheet via sheet.getRange(2, indexId + 1, newId.length).setValues(newId), you can pass the newID value to the createSubfolder function through a parameter. Just edit last lines of the autoid_(sheet) function and call your createSubfolder(newId) function as seen below:
UPDATED
See this edited full script below from the referenced post:
/**
*
* #param {GoogleAppsScript.Spreadsheet.Sheet} sheet
*/
function autoid_(sheet) {
var data = sheet.getDataRange().getValues();
if (data.length < 2) return;
var indexId = data[0].indexOf('ID');
var indexDate = data[0].indexOf('DATE');
if (indexId < 0 || indexDate < 0) return;
var id = data.reduce(
function(p, row) {
var year =
row[indexDate] && row[indexDate].getTime
? row[indexDate].getFullYear() % 100
: '-';
if (!Object.prototype.hasOwnProperty.call(p.indexByGroup, year)) {
p.indexByGroup[year] = [];
}
var match = ('' + row[indexId]).match(/(\d+)-(\d+)/);
var idVal = row[indexId];
if (match && match.length > 1) {
idVal = match[2];
p.indexByGroup[year].push(+idVal);
}
p.ids.push(idVal);
p.years.push(year);
return p;
},
{ indexByGroup: {}, ids: [], years: [] }
);
// Logger.log(JSON.stringify(id, null, ' '));
var newId = data
.map(function(row, i) {
if (row[indexId] !== '') return [row[indexId]];
if (isNumeric(id.years[i])) {
var lastId = Math.max.apply(
null,
id.indexByGroup[id.years[i]].filter(function(e) {
return isNumeric(e);
})
);
lastId = lastId === -Infinity ? 1 : lastId + 1;
id.indexByGroup[id.years[i]].push(lastId);
return [
Utilities.formatString(
'%s-%s',
id.years[i],
('000000000' + lastId).slice(-3)
)
];
}
return [''];
})
.slice(1);
//Instead of setting the value on sheet, add createSubfolder function below:
createSubfolder(newId);// Pass the newID value to the createSubfolder() parameter (inside the parenthesis) to "generatedID" as seen
}
function createSubfolder(generatedID){ // Edit your createSubfolder function with a parameter named generatedID (inside the parenthesis)
var dropbox = generatedID + Utilities.formatDate(new Date(), "US/Eastern", "_yyyy-MM-dd");
}

How can i modify the name to an uploaded google form file?

I`m trying to use the bellow script code to modify the name to an attachment from google form using another text and i receive the following error:
"ReferenceError: nameColumn is not defined at rename(Code:26:22)"
What i`m doing wrong?
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
// Notice the second 2, this is to avoid the Timestamp Column
var searchRange = sheet.getRange(2,2, lastRow-1, lastColumn-1);
// Replace with your values (Column A=1, B=2, etc...)
var nameColumn = 2; // B
var urlColumn = 3; // C
// Calculating index for array
nameColumn -= 2;
urlColumn -= 2;
}
function last() {
var lastRowContents=sheet.getRange(lastRow,2,1,sheet.getLastColumn()).getValues()[0];
rename(lastRowContents);
}
function rename(row) {
// Using the first field, Name (Index 0 becuse of the array, calculated above)
// ** Even though the Name field is the second column, we see it as the first one since
// we ignored the timestamp column in the searchRange **
var userName = row[nameColumn];
var url = row[urlColumn];
// Retrieve the ID from the URL
var Id = url.split('=')[1];
// Adapt this newFileName to your needs
var newFileName = userName;
// Get the file and rename it
DriveApp.getFileById(Id).setName(newFileName);
Logger.log("Renamed file with ID " + Id + " to " + newFileName);
};
In the description of forms (https://developers.google.com/apps-script/reference/forms/item) I did not find any access to attachments. I created a form with one question and inserted an image. I ran this code to see if the script had access to the image. The script only found one item - the unnamed question.
function exploreFormItems() {
const formId = '1EuPT6qNHfAWkbZhnlEpi1WPgMaIVPZDybM8h86jX3gg';
let form = FormApp.openById(formId);
let itemArr = form.getItems();
let idx = 0;
itemArr.forEach (function(item) {
console.log ('idx : ', idx );
console.log (' id : ', item.getId() );
console.log (' index: ', item.getIndex() );
console.log (' title: ', item.getTitle() );
console.log (' type : ', item.getType() );
console.log (' help : ', item.getHelpText() );
++idx;
} );
}
If you are trying to rename the form itself I found this https://stackoverflow.com/questions/47375841/how-to-change-a-file-name-of-a-google-form-bu-apps-script#:~:text=For%20a%20google%20spreadsheet%20it,rename(%22new%20name%22)%3B

How to export Mixpanel raw data to Google Sheet with Mixpanel's API?

Here is a solution to get raw data from Mixpanel to fill a Google spreadsheet. (It's not really a question :-) )
Get your Mixpanel Api secret key: https://help.mixpanel.com/hc/en-us/articles/115004490503-Project-Token-API-Key-API-Secret
Create google spreadsheet or open existing one.
2.1 Open the scripts editor
[Tools] -> [Script Editor]
2.2 Create fonction e.g: exportRawData(){ }
2.3 Prepare an Http Header like that:
/**
* Call Mixpanel raw export endpoint
**/
exportRawData(){
// Prepare header with the secret key
var API_SECRET = "Replace by your Secret Key";
var headers = {
"Authorization" : "Basic " + Utilities.base64Encode(API_SECRET)
};
var params = {
"method":"GET",
"headers":headers
};
// prepare call args
// WARNING: date are yyyy-mm-dd format
// replace fixed date by dynamic range (now - 7 days or other)
var url = "https://data.mixpanel.com/api/2.0/export/?from_date=2018-11-07&to_date=2018-11-14";
Logger.log('Call Mixpanel:' + url);
var response = UrlFetchApp.fetch(url, params);
var rawData = response.getContentText();
Logger.log('Response length:' + rawData.length);
var items = rawData.split("\n");
// prepare the array that it will be pushed into the google spreadsheet
var structuredData; // the json decoded data
var row;
var outer = []; // array of rows
for ( i in items ){
structuredData = JSON.parse( items[i] );
// In this example, we take Mobile usages only
// with Event, Browser, User
if ( structuredData.properties.$browser.indexOf('Mobile') >= 0 ){
row = new Array(structuredData.event, structuredData.properties.$browser, structuredData.properties.distinct_id);
// push the single row into the array of rowS
outer.push(row);
} //eo fi
} //eo rof
pushInSpreadsheet('Mobile Usage', outer);
} // end of exportRawData
/**
* Push data to the Google Spreadsheet
* sheetName: String
* data: Array of rows
* return: nothing
**/
function pushInSpreadsheet(sheetName, data){
Logger.log("push " + data.length + " item(s) into to sheet " + sheetName);
// Prepare the sheet into the active spread sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// select of exist or create new one
var sheet = ss.getSheetByName(sheetName) ||
ss.insertSheet(sheetName, 1);
sheet.clear();
// The expected structure for the horizontal range setting is:
// [ ["cell A1"],["cell B1"],["cell C1"] ]
var range = sheet.getRange(1, 1, data.length, data[0].length );
range.setValues( data );
}
I hope that it will be helpful. Any comments are welcome.

google sheet script inserting data into sheet

So i have a script the gets some data from a server.
I want my script to publish the data to a sheet name "market items".
I got this working if I'm running the script directly from the sheet by using =getMarketItemsTrigger(1).
It posts all 11,669 items to my sheet.
The problem with this is that it refreshes every time the sheet is reloaded; I need it to only run once a month.
I've been trying to create a script which needs no reference in the given sheet but posts directly to a pre-named sheet but I can't figure out how I can get the data into the sheet
this is the script file i'm using
var version = '9a'
function getMarketItemsTrigger(refresh)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Market Items");
Logger.log(sheet.getMaxColumns());
Logger.log(sheet.getMaxRows());
sheet.clear();
if(sheet.getMaxColumns()>2){
Logger.log('deleting colums');
sheet.deleteColumns(2, sheet.getMaxColumns()-2);
}
if(sheet.getMaxRows()>2){
Logger.log('deleting rows');
sheet.deleteRows(2,sheet.getMaxRows()-1);
}
var marketItemsEndpoint = 'https://crest-tq.eveonline.com/market/types/';
var marketItemsResponse = JSON.parse(fetchUrl(marketItemsEndpoint));
var totalPages = marketItemsResponse['pageCount'];
var itemList = [];
var headers = ['Item Name', 'ID'];
itemList.push(headers);
for (var currentPage = 1; currentPage <= totalPages; currentPage++)
{
Logger.log('Processing page ' + currentPage);
var marketItems = marketItemsResponse['items'];
for (var itemReference in marketItems)
{
var item = marketItems[itemReference];
itemList.push([item['type']['name'], item['id']]);
}
if (currentPage < totalPages)
{
var nextEndpoint = marketItemsResponse['next']['href'];
marketItemsResponse = JSON.parse(fetchUrl(nextEndpoint));
}
}
//sheet.insertRows(1,itemList.length+1);
// var range = sheet.getRange(1, 1,itemList.length+1,3);
// for(var i = 1;i<itemList.length;i++){
// range.getCell(i, 1).setValue([itemList]);
// range.getCell(1, i).setValue(itemList.);
// }
// Logger.log("don");
//sheet.getRange(1, 1, 1, itemList.length).setValues(itemList);
// sheet.getRange(itemList.length+1, 2).setValues(itemList);
// sheet.getDataRange().setValues([itemList]);
// sheet.appendRow(itemList);
// sheet.getRange(12+totalPages, 1, itemList.length, 1).setValues(itemList);
return itemList;
}
/**
* Private helper method that wraps the UrlFetchApp in a semaphore
* to prevent service overload.
*
* #param {url} url The URL to contact
* #param {options} options The fetch options to utilize in the request
*/
function fetchUrl(url)
{
if (gcsGetLock())
{
// Make the service call
headers = {"User-Agent": "Google Crest Script version " + version + " (/u/nuadi #Reddit.com)"}
params = {"headers": headers}
httpResponse = UrlFetchApp.fetch(url, params);
}
return httpResponse;
}
/**
* Custom implementation of a semaphore after LockService failed to support GCS properly.
* Hopefully this works a bit longer...
*
* This function searches through N semaphores, until it finds one that is not defined.
* Once it finds one, that n-th semaphore is set to TRUE and the function returns.
* If no semaphore is open, the function sleeps 0.1 seconds before trying again.
*/
function gcsGetLock()
{
var NLocks = 150;
var lock = false;
while (!lock)
{
for (var nLock = 0; nLock < NLocks; nLock++)
{
if (CacheService.getDocumentCache().get('GCSLock' + nLock) == null)
{
CacheService.getDocumentCache().put('GCSLock' + nLock, true, 1)
lock = true;
break;
}
}
}
return lock;
}
/**
* Private helper function that will check for a new version of GCS.
*/
function versionCheck()
{
var versionEndpoint = 'https://raw.githubusercontent.com/nuadi/googlecrestscript/master/version';
var newVersion = fetchUrl(versionEndpoint);
if (newVersion != null)
{
newVersion = newVersion.getContentText().trim();
Logger.log('Current version from Github: ' + newVersion);
var message = 'You are using the latest version of GCS. Fly safe. o7';
var title = 'No updates found';
if (newVersion > version)
{
message = 'A new version of GCS is available on GitHub.';
title = 'GCS version ' + newVersion + ' available!';
}
SpreadsheetApp.getActiveSpreadsheet().toast(message, title, 120);
}
}
All the code in the function getMarketItemsTrigger that's commented out is what I have tyred without luck .
The short version of this question is how can i post the values in itemList to column a and b in sheet market items
You can write the array itemList to the sheet by adding:
//your code
ss.getSheetByName('name_of_sheet_here')
.getRange(1, 1, itemList.length, itemList[0].length)
.setValues(itemList)
//more code (if needed)
} //end of code
-->> change sheet name and range to suit.
There are two ways to do this. If you did want it to run as a custom function those have access to the script property service. You could save a time stamp in the script properties and check it every time the custom function runs.
https://developers.google.com/apps-script/reference/properties/
https://developers.google.com/apps-script/guides/sheets/functions#using_apps_script_services
The second is to create a time trigger to run the code as a cron job every month.
https://developers.google.com/apps-script/guides/triggers/installable#time-driven_triggers
https://developers.google.com/apps-script/guides/triggers/installable#managing_triggers_manually

Removing invalid named ranges in GAS

I have defined some named ranges in a sheet that I later delete. Afterwards, the ranges remain in the sidebar "Data->Named ranges...", with the range "#REF". I would like to delete them because I don't want them to accumulate.
They are not listed in SpreadsheetApp.GetActiveSpreadsheet.getNamedRanges().
How can I delete them programatically?
An alternative solution would be how to define a named range that is removed when a sheet is deleted. This happens if you have a named range in a sheet that is duplicated - the named range has a name like "'Sheet1Copy'!RangeName", but it's not possible to define a name like this.
Use removeNamedRange(name) to remove a Named Range. It will work even with Named Ranges that has #REF! as range and are not returned by SpreadsheetApp.getActiveSpreadsheet().getNamedRanges().
In order to make easier to maintain your spreadsheets free of Named Ranges with #REF! as range, keep a list of your Named Ranges. You could use an auxiliary spreadsheet for that.
UPDATED BELOW
The accepted answer works fine if you are removing a unique Named Range, but not if you have more than one Range sharing the same Name (i.e. Sheet-scoped Named Ranges vs Spreadsheet-scoped Named Ranges).
The first instance of the Named Range will probably be scoped to the entire Spreadsheet, whereas any copies of the Sheet (that contain the original Named Range) will feature Named Ranges that are scoped to the copied Sheet (e.g. 'SheetCopy'!NamedRange instead of NamedRange).
If you want to remove Sheet-scoped Named Ranges whose references are no longer valid, try running the following script from the Script Editor:
function removeDeadReferences()
{
var activeSS = SpreadsheetApp.getActiveSpreadsheet();
var sheets = activeSS.getSheets();
var sheetNamedRanges, loopRangeA1Notation;
var x, i;
// minimum sheet count is 1, no need to check for empty array, but why not
if (sheets.length)
{
for (x in sheets)
{
sheetNamedRanges = sheets[x].getNamedRanges();
// check for empty array
if (sheetNamedRanges.length)
{
for (i = 0; i < sheetNamedRanges.length; i++)
{ // get A1 notation of referenced cells for testing purposes
loopRangeA1Notation = sheetNamedRanges[i].getRange().getA1Notation();
// check for length to prevent throwing errors during tests
if (loopRangeA1Notation.length)
{ // check for bad reference
// note: not sure why the trailing "!" mark is currently omitted
// ....: so there are added tests to ensure future compatibility
if (
loopRangeA1Notation.slice(0,1) === "#"
|| loopRangeA1Notation.slice(-1) === "!"
|| loopRangeA1Notation.indexOf("REF") > -1
)
{
sheetNamedRanges[i].remove();
}
}
}
}
}
}
}
Edit / Update
I recently needed to run this script again, and Google must've made changes to the way the .getRange() function works. The previous script will not work if the Named Range contains an invalid reference.
Also, after re-reading the initial question, I feel like I was too focused on the Sheet-scoped aspect of the answer.
The OP seemed to be after more of an automated experience, which the Accepted Answer does not provide. In addition to not requiring an externally maintained list of Named Ranges, the following script will remove both Sheet-scoped and Spreadsheet-scoped Named Ranges that contain invalid references.
Here's the updated script, tested to work with the V8 engine:
function removeDeadReferences()
{
var activeSS = SpreadsheetApp.getActiveSpreadsheet();
var sheets = activeSS.getSheets();
var sheet;
var sheetName;
var sheetNamedRanges, sheetNamedRange, sheetNamedRangeName;
var loopRange, loopRangeA1Notation;
var x, i;
// minimum sheet count is 1, no need to check for empty array
for (x in sheets)
{
sheet = sheets[x];
// for logging
sheetName = sheet.getName();
sheetNamedRanges = sheet.getNamedRanges();
// check for empty array
if (sheetNamedRanges.length)
{
for (i = 0; i < sheetNamedRanges.length; i++)
{
sheetNamedRange = sheetNamedRanges[i];
// for logging
sheetNamedRangeName = sheetNamedRange.getName();
// v8 engine won't allow you to get range if it is invalid
try {
loopRange = sheetNamedRange.getRange();
}
catch (error)
{
Logger.log(error);
loopRange = null;
}
// get A1 notation of referenced cells for testing purposes
loopRangeA1Notation = (
loopRange != null
? loopRange.getA1Notation()
: false
);
// check for bad reference
// added tests to ensure future compatibility
// but any of these should suffice
// comment out ones you don't want to test for
if (
loopRangeA1Notation == false
|| loopRangeA1Notation.slice(0,1) === "#"
|| loopRangeA1Notation.slice(-1) === "!"
|| loopRangeA1Notation.indexOf("REF") > -1
)
{
Logger.log("The named range, '" + sheetNamedRangeName + "', within the Sheet named, '" + sheetName + "', was removed.");
sheetNamedRange.remove();
}
}
}
}
}
The function below loops sheets and deletes "bad" named ranges.
You may see it live here. Make your own copy and go to menu:
⚡ Test Automation > 🍏 Sheets > 🦶 Delete Bad Named Ranges
please try
function test_deleteBadNamedRanges() {
var sets = {
delimiter: '|',
file: '', // leave blank to use active spreadsheet
sheet_names: 'nonono|Sheet1|Sheet2', // note put empty string or null to use all sheets
remove_copies: true, // set true to delete 'copy of sheet'!named_ranges
success_message: '🦶Removed "bad" named ranges!'
}
var result = deleteBadNamedRanges_(sets);
console.log(result);
console.log(sets);
}
function deleteBadNamedRanges_(sets) {
var book, msg = '';
try {
if (!sets.file || set.file === '') {
book = SpreadsheetApp.getActive();
} else {
book = SpreadsheetApp.openById(sets.file);
}
} catch (err) {
msg = '🤪Could not get the book for some reason.'
msg += ' The error is: ' + err;
return msg;
}
if (!book) {
msg = '🤪The book object could not be retrieved.'
return msg;
}
/** function to get sheets by names */
var sheets = [];
var errors_log = [];
var getSheetsByNames_ = function(book, names) {
var sheets = [], sht, msg;
for (var i = 0; i < names.length; i++) {
mag = 'ok';
try {
sht = book.getSheetByName(names[i]);
} catch (err) {
msg = '❌no sheet ' + names[i] + '. ';
msg += 'Err: ' + err;
sht = false;
}
if (!sht) {
msg = '❌not found sheet ' + names[i]
}
sheets.push(sht);
errors_log.push(msg);
}
return sheets;
}
/** get sheets */
if (!sets.sheet_names || sets.sheet_names === '') {
sheets = book.getSheets();
} else {
sheets = getSheetsByNames_(
book,
sets.sheet_names.split(sets.delimiter));
}
/** delete bad named ranges for each sheet */
var res = [];
for (var i = 0; i < sheets.length; i++) {
sets.sheet = sheets[i];
if (sets.sheet) {
res.push(deleteBadNamedRangesSheet_(sets))
} else {
res.push(errors_log[i]);
}
}
sets.sheet = '_end_of_loop_';
var result = sets.success_message + '\\n\\n' +
res.join('\\n');
return result;
}
/**
*
* delete bad ranges for 1 sheet
*/
function deleteBadNamedRangesSheet_(sets) {
var sheet = sets.sheet;
var sheet_name = sets.sheet.getName();
// for logs
if (!sets.removed_ramed_ranges) {
sets.removed_ramed_ranges = [];
}
/** function to remove named range
* and log it to memory
*/
var count_deleted = 0;
var add2removed_ = function(namedrange, a1, name) {
namedrange.remove();
sets.removed_ramed_ranges.push({
sheet_name: sheet_name,
name: name,
a1: a1
});
count_deleted++;
return 0;
}
/** loop and check named ranges */
var ranges = sheet.getNamedRanges();
var a1 = '', nr, name = '', check_copy;
for (var i = 0; i < ranges.length; i++) {
nr = ranges[i];
a1 = nr.getRange().getA1Notation();
name = nr.getName();
// remove copy of named range
if (sets.remove_copies) {
check_copy = name
.replace(sheet_name, '')
.substring(0,3);
if (check_copy === "''!") {
add2removed_(nr, a1, name);
}
}
// remove named range
if (a1 == '#REF!') {
add2removed_(nr, a1, name);
}
}
return '✔️Sheet ' + sheet_name + ', deleted: ' + count_deleted;
}