如何通过 Apps 脚本删除带有选项卡名称的命名范围

How to delete Named Ranges with Tab names via Apps Script

我正在尝试清理我不再需要的一系列命名范围的传播sheet,并留下我仍在使用的少数几个。所有这些命名范围都包含选项卡名称,因为它们源自模板选项卡(名为 tmp),其他选项卡从该模板中复制。即使在我从 sheet 中删除所有衍生选项卡并仅留下 tmp 之后,'tmp'!出现在范围的名称中,既显示在命名范围侧边栏中,也出现在 getNamedRanges() 上。

当我尝试有选择地删除已排除的命名范围时,无论我如何指定范围的名称,我都会收到错误消息,指出不存在这样的命名范围。基本上,我反馈的信息与 getNamedRanges() 和 getRange().getSheet().getSheetName() 给我的信息相同,但一路上都是乱码。

问题在以下测试片段中被隔离,涉及呈现选项卡名称周围的单引号。我尝试了几种方法,包括用斜杠转义单引号,并在代码中添加了我在行 targetDoc.removeNamedRange(namedRange).

const analyzerDoc = '1pYgcX2dxzHd4cCofy0RFZTzEl36QesiakMGIqCC2QlY'
const openAnalyzerDoc = SpreadsheetApp.openById(analyzerDoc)


function testDeleteNamedRange (){
  var docUrl = openAnalyzerDoc.getRangeByName('docUrl').getValue();
  var targetDoc = SpreadsheetApp.openByUrl(docUrl);
  // var namedRange = 'dyCl_MoodEntries'         // The named range "dyCl_MoodEntries" does not exist.
  // var namedRange = 'tmp!dyCl_MoodEntries'     // The named range "tmp!dyCl_MoodEntries" does not exist.
  // var namedRange = "'tmp'!dyCl_MoodEntries"   // The named range "'tmp'!dyCl_MoodEntries" does not exist.
  // var namedRange = "\'tmp\'!dyCl_MoodEntries" // The named range "'tmp'!dyCl_MoodEntries" does not exist.
  targetDoc.removeNamedRange(namedRange);
}

此错误妨碍了一个较长的函数,该函数工作正常,但对于此测试函数中隔离的部分。

较长的函数获取要从此 sheet 中删除的范围的名称和选项卡:

正确的做法是什么?谢谢!

此函数将删除范围名称中具有 sheet 名称的所有命名范围。

function deleteAllNamedRange() {
  const ss = SpreadsheetApp.getActive();
  ss.getNamedRanges().filter(r => ~r.getName().indexOf(r.getRange().getSheet().getName())).forEach(r => r.remove());
}

我自己的问题有了答案。可能有不止一种解决方案,但我选择回避我面临的挑战,而不是按名称指定命名范围,我将根据它们在文档命名范围中的位置来指定它们,并简单地使用 remove( ) 而不是 removeNamedRange(namedRange)。我对涉及 forEach 的推荐方法太着迷了,以至于我忘记了 getNamedRanges() 的结果不是一个对象,而是一个数组。

然后解决方案在于修改我从 getNamedRanges() 的结果中收集名称和其他信息的过程。我没有使用 forEach,而是遍历 getNamedRanges() 的结果,在获得有关每个命名范围的所需信息的同时,我还记录了循环迭代,从而获得了每个命名范围的索引号。

我像以前一样继续,将此信息粘贴到选项卡中,我可以在其中 select 要删除的范围。

然后我的删除函数以相反的顺序直接遍历命名范围,并根据我在该分析选项卡中勾选的范围检查循环迭代。

我已经在示例文档中对此进行了测试;你可以查看它 here.

在此演示中,所有函数都在同一个文档中,因此我使用 getActive() 而不是 openByUrl。

此文档有 3 个标签,分别名为 Sheet1、Sheet2 和 Sheet3。每个选项卡都有 3 个命名范围,分别命名为 Moe、Larry 和 Curly。还有一个 Tab NamedRanges,下面的函数将 Named Range 收集到:

function getnamedRanges() {
  var namedRanges = SpreadsheetApp.getActive().getNamedRanges();
  var namedRangeData = [];
  for (i=0; i<namedRanges.length; i++) {
    var namedRange = namedRanges[i];
    var nrName = namedRange.getName();
    var nrRange = namedRange.getRange().getA1Notation();
    namedRangeData.push([nrName,nrRange,i])
  }
  SpreadsheetApp.getActive().getSheetByName('NamedRanges').getRange(2,1,namedRangeData.length,3).setValues(namedRangeData)
}

这是 运行 该函数之后的命名范围选项卡,并选择 3 个要删除的命名范围:

接下来,这是删除 selected 命名范围的函数:

function deleteSelectedNamedRanges () {
  var namedRangeData = SpreadsheetApp.getActive().getSheetByName('NamedRanges').getDataRange().getValues();
  namedRangeData.shift(); // Remove header row data.
  var rangesToDelete = namedRangeData.filter(function(nrDatum) {if (nrDatum[3]==true) return nrDatum});

    // [3] equivalates to Column D, the checkboxes where I select which Named Ranges to delete.
    console.log (rangesToDelete.map(value => value[0])); // [ 'Sheet3\'!Moe', 'Sheet2\'!Curly', 'Sheet1\'!Moe' ]
    console.log (rangesToDelete.map(value => value[2])); // [ 0, 1, 5 ] // [2] is the index number of the Named Ranges.

    /* The order here derives from how values in Tab Named Ranges happen to be sorted. 
    In this instance, I have not changed that order, so the Named Ranges To Delete are in ascending order.
    For one thing, this is the opposite of what we want; 
    for another, I want to be able to sort the Named Range Tab freely before making my selections.
    So, we must sort this data in DESCENDING order. */

  rangesToDelete.sort(function(value1,value2){if (value1[2]<value2[2]) return 1; if (value1[2]>value2[2]) return -1; return 0});

    console.log (rangesToDelete.map(value => value[0])); // [ 'Sheet1\'!Moe', 'Sheet2\'!Curly', 'Sheet3\'!Moe' ]
  var rangesToDeleteIndexNumbers = rangesToDelete.map(value => value[2])
    console.log (rangesToDeleteIndexNumbers); // [ 5, 1, 0 ]
  var namedRanges = SpreadsheetApp.getActive().getNamedRanges();
  for (i=namedRanges.length-1; i>=0; i--) { 

    /* We must loop in descending order because deleting Named Ranges will change the index numbers
     of all Named Ranges that come after each we delete. */

    if (rangesToDeleteIndexNumbers.indexOf(i) !== -1) {namedRanges[i].remove(); console.log ('Removed NR # '+i)} 

    // Delete Named Range if this iteration number can be found in rangesToDeleteIndexNumbers.
  }
}

在运行这个函数之后,可以看到去掉了3个Names Ranges,剩下6个:

如果您只想删除特定的命名范围,您可能会受益于尝试以下代码:

function testDeleteNamedRange() {
  var openAnalyzerDoc = SpreadsheetApp.openById('SPREADSHEET_ID');
  var namedRanges = ['NamedRange1', 'NamedRange2', 'NamedRange3', 'NamedRange4'];
  namedRanges.forEach(range => openAnalyzerDoc.removeNamedRange(range));
}

前运行代码

执行testDeleteNamedRange函数后

说明

您要删除的命名范围已添加到数组中;因此,通过遍历此数组,已成功从电子表格中删除每个命名范围。

removeNamedRange 方法需要一个 String 类型的对象,它是需要删除的范围的名称。

至于您提到的 getNamedRanges,此方法最终会以包含 NamedRange 个对象的数组形式从电子表格中检索所有命名范围 - NamedRange[]

参考