自动更新命名范围

Update named range automatically

我一直很难弄清楚这个问题。我意识到对于遵循 GAS 标签的人来说,这可能比平时更基本,但是非常感谢任何帮助。

如果我将更大的任务分解成多个组成部分,我现在的目标是自动更新几个命名范围。

价差 sheet 上有一个名为 "DataImport" 的标签。 DataImport 有 10 列,所有 1000 行。每列都有一个命名范围,例如卷心菜 (A2:A1000)、狗 (B2:B1000) 等

There is a script attached to a new menu item "Update Data" that when selected imports a csv file into DataImport tab meaning that the length of the data set will grow.

如何告诉 sheet 将每个命名范围更新为数据长度?因此,如果原始命名范围 "cabbages" 是 A2:A1000 并且在更新后数据现在实际上只要 A2:A1500,我将如何告诉 sheet 更新范围卷心菜?

我在网上找到了一段代码并开始 fiddle 使用它:

function testNamedRange() {
   var ss = SpreadsheetApp.getActiveSpreadsheet();
   var range = ss.getRange('DataImport!A:A');
   var data_len = range.length;
   SpreadsheetApp.getUi().alert(data_len); // "alert just gives "undefined"
   ss.setNamedRange('TestRange', range);
   var rangeCheck = ss.getRangeByName('TestRange');
   var rangeCheckName = rangeCheck.getA1Notation();
}

我的想法是,如果我可以使用自定义菜单函数获取更新后的数据长度,然后我可以使用 setNamedRange() 来更新卷心菜范围。

我真的迷路了,我想这比我想象的要简单。

如何将命名范围 cabbages 更新为 UpdateData 列 A 中的数据长度?

编辑:重要

在公式 中使用 INDIRECT("rangeName") 而不仅仅是 rangeName。 以编程方式扩展范围的唯一方法是删除它,然后使用新定义将其添加回来。此过程打破了公式和 returns #ref 而不是范围名称。这应该是一个不必要的解决方法。如果您同意,请在 https://code.google.com/p/google-apps-script-issues/issues/detail?id=5048

上加注星标和问题跟踪器
=sum(indirect("test1"),indirect("test3"))

通过检查命名范围中的最后一行是否与 sheet 中的最后一行相同来模拟开放式命名范围。如果不是,则调整命名范围,使命名范围中的最后一行与 sheet.

中的最后一行相同

应该与 on open 和 on change 事件一起使用。

function updateOpenEndedNamedRanges() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();

  // names of open-ended ranges
  var openEndedRangeNames = ["test1", "test2", "test3", "s2test1" ];

  for(i in openEndedRangeNames) {
    var rName = openEndedRangeNames[i];
    try{
      var r = ss.getRangeByName(rName);
      }
    catch(err) {
      GmailApp.sendEmail("me@gmail.com",
                         rName + " -- Cannot find",
         "Trying to update open-ended ranges after rows added. \n"
         + "Unable to find range name-- "+ rName 
         + " -- in ss ( " + ss.getName() + " ) "
         + "\n If it is not needed please remove it " 
         + "from array \n openEndedRangeNames[] \n in the function \n"
         + "updateOpenEndedNamedRanges()");
      continue;
      }
    var rlr = r.getLastRow();
    var s = r.getSheet();
    var slr = s.getMaxRows();
    if(rlr==slr ) continue;
    var rfr = r.getRow();
    var rfc = r.getColumn();
    var rnc = r.getNumColumns();
    var rnr = slr - rfr + 1;
    ss.removeNamedRange(rName);
    ss.setNamedRange( rName, s.getRange(rfr, rfc, rnr, rnc ));
    }
}


function ssChangeEvent(change) {
 // changeType (EDIT, INSERT_ROW, INSERT_COLUMN, REMOVE_ROW, 
 //      REMOVE_COLUMN, INSERT_GRID, REMOVE_GRID, or OTHER)
  switch(change.changeType) {
    case "INSERT_ROW":
      updateOpenEndedNamedRanges();
      break;
    default:
      Logger.log(change.changeType + " detected. No action taken ");
      }
  }

添加行时将 ssChangeEvent(change) 设置为 运行

资源>这个项目触发

提供我编写的这个函数来处理命名范围的动态调整大小:

function resizeNamedRange(rangeName, addRows, addCols) {
/* expands (or decreases) a range of a named range. 
rows and columns to add can be negative (to decrease range of name). Params:
rangeName - name of range to resize.
addRows - number of rows to add (subtract) from current range.
addCols - number of columns to add (subtract) from current range.
Call example: resizeNamedRange("Products",1,0);
   */

  var sh = SpreadsheetApp.getActiveSpreadsheet();

  try {
    var oldRange = sh.getRangeByName(rangeName);
    var numRows = oldRange.getNumRows() + addRows;
    var numCols = oldRange.getNumColumns() + addCols;
    if (numRows < 1 || numCols <1) {
      Logger.log("Can't resize a named range: minimum range size is 1x1.");
      return;
    }
    sh.setNamedRange(rangeName, oldRange.offset(0,0,numRows, numCols));

  } catch (e) {
    Logger.log ("Failed resizing named range: %s. Make sure range name exists.", rangeName); 
  }
}

也许我遗漏了什么,但下面的函数接受了一个范围名称和它应该包含的范围。如果范围名称已经存在,它会将范围更新为传递的值。如果范围名称不存在,它会使用传递的范围值创建它。

也关于“#REF!” sheet 中的问题。您可以执行查找和替换并勾选 "find in formulas" 的框。放“#REF!”在查找和替换框中的命名范围名称。这假设只删除了一个命名范围,并且没有其他不相关的#REF!错误。这种方法帮助我在短短几分钟内修复了 spreadsheet,错误分布在 8 个不同的 sheet 和 20 多个公式上。

/**
 * Corrects a named range to reflect the passed range or creates it if it doesn't exist.
 *
 * @param {string} String name of the Named Range
 * @param {range} Range (not string, but range type from Sheet class)
 * @return {void || range} returns void if Named Range had to be created, returns NamedRange class if just updated. This needs improvement.
 * @customfunction
 */
function fixNamedRange (name, range) {
  var i = 0;
  var ss = SpreadsheetApp
           .getActiveSpreadsheet();
  var ssNamedRanges = ss.getNamedRanges();

  for (i = 0; i<ssNamedRanges.length && ssNamedRanges[i].getName() != name; i++) {};

  if (i == ssNamedRanges.length) {
    return (ss.setNamedRange(name, range));
  } else {
    return (ssNamedRanges[i].setRange(range));
  }
}

我找到了解决办法!

我有一个带有下拉列表的单元格,其中包含公司在系统上注册的所有客户,如果我们输入的名称没有出现在列表中,则执行 newClient 函数。基本上我们使用 SpreadsheetApp.getUi() 来保存新信息。一旦我们引入了客户端数据,函数就会在客户端的 sheet 上创建一个新行,并在最后一行的提示中输入信息。完成后,自动更新下拉列表。

真正的函数在一个大函数内部,如果需要它会调用 newClient,所以真正的函数是 newClient(client, clients),在这个例子中我放置了变量以使其更容易。

希望有用!

function newClient() {
  var ss = SpreadsheetApp.getActive().getSheetByName('Clients'); // Sheet with all the client information, name, city, country...
  var client = 'New company';
  var clients = ss.getRange('A2:A').getValues();
  var ui = SpreadsheetApp.getUi();
  ui.alert('Client '+client+' does not exist, enter the next information.');
  var city = ui.prompt('Enter city').getResponseText();
  var country = ui.prompt('Enter country').getResponseText();
  client = client.toUpperCase();
  city = city.toUpperCase();
  country = country.toUpperCase();
  ui.alert('Here is the information you entered about '+client+':'+'\n\n'+'City: '+city+'\n\n'+'Country: '+country)
  ss.insertRowAfter(ss.getLastRow()); // Insert a row after the last client
  ss.getRange('A'+(clients.length+2)).setValue(client); // Let's suppose we have 150 clients, on the first row we have the titles Client, City, Country, then we have the 150 clients so the last client is on row 151, that's why we enter the new one on the 152
  ss.getRange('B'+(clients.length+2)).setValue(city);
  ss.getRange('C'+(clients.length+2)).setValue(country);
  var namedRanges = SpreadsheetApp.getActive().getNamedRanges(); // We get all the named ranges in an array
  for (var i = 0; i < namedRanges.length; i++) {
    var name = namedRanges[0].getName();
    if (name == 'Clients') { // All the clients are stored on 'Clients' named range
      var range = ss.getRange('A2:A'); // Update the range of the 'Clients' named range
      namedRanges[i].setRange(range);
    }
  }
  ui.alert('Client created, you can find it on the drop down list.');
}