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");
}
Related
I am trying to get results of following function in cells in google sheet.
I can get the results in script log but i need it in cells .. can someone add to this syntax so that its results can be placed in cells.
function scrapeDemo(url) {
var html = UrlFetchApp.fetch(url).getContentText();
var res = html.match(/<div class="cr_data rr_stockprice module">.+?(<table .+?<\/table>)/);
var document = XmlService.parse(res[1]);
var root = document.getRootElement();
var trNodes = root.getChild('tbody').getChildren();
trNodes.forEach((trNode) => {
var tdNodes = trNode.getChildren();
var fieldName;
var fieldValue;
tdNodes.forEach((tdNode, idx) => {
if (idx % 2 === 0) {
fieldName = tdNode.getValue().trim();
} else {
fieldValue = tdNode.getValue().trim();
console.log( fieldName + " : " + fieldValue );
}
} );
} );
}
I am trying to get the result that are currently available in script log in cells. When I put scrapeDemo(url) in a cell It should give the data. It can be as a table with Fieldnames in one column and Fieldvalue in next column.
The script in the question body doesn't include a return statement, but A Google Sheets custom function should have one returning a value / object supported by Google Sheets (number, string, boolean, date, or an Array of Arrays of supported values/objects), example:
/**
* If a and b are numbers these values will be added
* If a or b are strings the these values will be concatenated
*/
function myFunction(a,b){
return a + b;
}
/**
* Returns a Matrix of X having rowNum by colNum
*
*/
function XMATRIX(rowNum,colNum){
var output = [];
for(var i = 0; i < rowNum; i++){
output[i] = [];
for(var j = 0; j < colNum; j++){
output[i][j] = 'X';
}
}
return output
}
Resources
https://developers.google.com/apps-script/guides/sheets/functions
If you have a range or want to build a range of data and you are executing your script in a google sheet, you can do it like:
var sheet = SpreadsheetApp.getActive().getSheetByName("NameOfTheSheet");
sheet.getActiveCell("A1").setValues(valueRange);
If you want to write value by value on its own. Use:
var sheet = SpreadsheetApp.getActive().getSheetByName("NameOfTheSheet");
sheet.setActiveRange("A1").setValues(value);
if you want to iterate in the Cells, then use:
...
sheet.setActiveRange("A" + i).setValues(value);
i++;
...
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
I'd like to create a report of storage usage for all my users. To do so I use AdminReports app, like so (found in google example, somewhere. Just had to adapt the "parameters" and the "row" arrays) :
function generateUserUsageReport() {
var today = new Date();
var oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
var timezone = Session.getScriptTimeZone();
var date = Utilities.formatDate(oneWeekAgo, timezone, 'yyyy-MM-dd');
var parameters = [
'accounts:gmail_used_quota_in_mb',
'accounts:drive_used_quota_in_mb',
'accounts:total_quota_in_mb ',
'accounts:used_quota_in_percentage'
];
var rows = [];
var pageToken;
var page;
do {
page = AdminReports.UserUsageReport.get('all', date, {
parameters: parameters.join(','),
maxResults: 500,
pageToken: pageToken
});
if (page.warnings) {
for (var i = 0; i < page.warnings.length; i++) {
var warning = page.warnings[i];
Logger.log(warning.message);
}
}
var reports = page.usageReports;
if (reports) {
for (var i = 0; i < reports.length; i++) {
var report = reports[i];
var parameterValues = getParameterValues(report.parameters);
var row = [
report.date,
report.entity.userEmail,
parseInt(parameterValues['accounts:drive_used_quota_in_mb']),
parseInt(parameterValues['accounts:gmail_used_quota_in_mb']),
parseInt(parameterValues['accounts:total_quota_in_mb']),
((parseInt(parameterValues['accounts:gmail_used_quota_in_mb'])+parseInt(parameterValues['accounts:drive_used_quota_in_mb']))/parseInt(parameterValues['accounts:total_quota_in_mb']))*100
];
rows.push(row);
}
}
pageToken = page.nextPageToken;
} while (pageToken);
if (rows.length > 0) {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
// Append the headers.
var headers = [['Date', 'User mail', 'Drive use','Gmail use', 'Total available',
'Total(%)']];
sheet.getRange(1, 1, 1, 6).setValues(headers);
// Append the results.
sheet.getRange(2, 1, rows.length, 6).setValues(rows);
Logger.log('Report spreadsheet created: %s', spreadsheet.getUrl());
} else {
Logger.log('No results returned.');
}
}
/**
* Gets a map of parameter names to values from an array of parameter objects.
* #param {Array} parameters An array of parameter objects.
* #return {Object} A map from parameter names to their values.
*/
function getParameterValues(parameters) {
return parameters.reduce(function(result, parameter) {
var name = parameter.name;
var value;
if (parameter.intValue !== undefined) {
value = parameter.intValue;
} else if (parameter.stringValue !== undefined) {
value = parameter.stringValue;
} else if (parameter.datetimeValue !== undefined) {
value = new Date(parameter.datetimeValue);
} else if (parameter.boolValue !== undefined) {
value = parameter.boolValue;
}
result[name] = value;
return result;
}, {});
}
The issue I have is that the parameters "accounts:drive_used_quota_in_mb" gives you the drive usage WITH the shared files (which is irrelevant to calculate the storage used by a user ( to determine whether he needs more space or not)).
I even tried to use 'accounts:used_quota_in_percentage' which seemed to be exactly what I need, but it calculate the percentage the same way i do : ((drive + mail)/total space)*100, and no way to ignore shared files to do so.
I'm working on the possibility to check every files of the drive, but you know the next problem : slowness.. (just for 1User with few docs, it take 1-2minutes)
Is there a way to do so by script, with another class, or something that is done for it in google that I didn't see ?
Thanks for your reading, forgive my english.
Ok, there is no issue, I just freaked out because a user was at 30Go consumption although he has his account for only 2month.
But after discussing with him, he did upload heavy files one week ago, and since, he deleted it.
And executing the script for only 2days ago gives the correct result, since he deleted these files between these two dates.
The reason of my mistake is that my script was providing stats that was 1 Week old (without me being conscious of that), and I was checking the veracity of theses stats on the web interface, that incidates nowadays stats.
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 **/
}
}
}
}
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;
}