【问题标题】:How to get range that has been moved?如何获得已移动的范围?
【发布时间】:2022-08-18 15:11:24
【问题描述】:

假设我得到范围 A2:B2:

sheet.getRange(\"A2:B2\").getValues();

但是我在 A1:B1 下添加了一行,所以现在我的值在 A3:B3 中

Apps 脚本是否可以动态捕获我的值现在在另一个范围内?

如果没有,关于如何动态获取移动行范围的任何替代想法?

    标签: google-apps-script google-sheets


    【解决方案1】:

    我相信你的目标如下。

    • 您想知道“A2:B2”范围的值是否被移动。

    在这种情况下,如何使用命名范围、OnChange 触发器和 PropertiesService?我认为当使用这些时,您的目标可能会实现。准备好示例脚本后,如下所示。

    用法:

    1. 创建一个命名范围。

    作为示例,请将单元格“A2:B2”的命名范围创建为“sampleNamedRange1”。 Ref

    2. 准备示例脚本。

    请将以下脚本复制并粘贴到电子表格的脚本编辑器中。还有,please install OnChange trigger to the function installedOnChange

    function installedOnChange(e) {
      var originalRange = "A2:B2";
      var nameOfNamedRange = "sampleNamedRange1";
    
      if (!["INSERT_ROW", "REMOVE_ROW"].includes(e.changeType)) return;
      var p = PropertiesService.getScriptProperties();
      var pv = p.getProperty("range");
      if (!pv) pv = originalRange;
      var range = e.source.getRangeByName(nameOfNamedRange);
      var a1Notation = range.getA1Notation();
      if (a1Notation != pv) {
        p.setProperty("range", a1Notation);
        Browser.msgBox(`Range was changed from ${pv} to "${a1Notation}". Original range is "${originalRange}".`);
      }
      // var values = range.getValues(); // The values are not changed because of the named range.
    }
    

    3. 测试。

    在这种情况下,请I added a row under A1:B1。这样,脚本将由 OnChange 触发器自动运行。而且,您可以看到对话框。您可以看到如下图所示的演示。

    笔记:

    • 这是一个简单的示例脚本。所以,请根据您的实际情况进行修改。

    参考:

    添加:

    从讨论中,我了解到您想使用命名范围的名称来检索特定工作表的命名范围。在这种情况下,示例脚本如下。

    使用工作表名称时

    const range = SpreadsheetApp.getActive().getSheetByName("sheetName").getRange("nameOfNamedRange");
    

    或者

    const range = SpreadsheetApp.getActive().getSheetByName("sheetName").getNamedRanges().find(n => n.getName() == "snameOfNamedRange").getRange();
    

    使用活动工作表时

    const range = SpreadsheetApp.getActiveSheet().getRange("nameOfNamedRange");
    

    或者

    const range = SpreadsheetApp.getActiveSheet().getNamedRanges().find(n => n.getName() == "snameOfNamedRange").getRange();
    

    【讨论】:

    • 这不是我的目标,但感谢您的努力。
    • @Karim 谢谢你的回复。我为我糟糕的英语水平道歉。关于That's not my goal but thanks for your effort.,为了正确理解你的目标,能否提供一下细节?借此,我想确认一下。如果您能原谅我糟糕的英语水平,我将不胜感激。
    • 感谢您的回复,无需为您的英语水平道歉。我想在值更改时为 getRange 动态分配一个值。就像 Excel/Sheets 通过公式将一个单元格链接到另一个单元格时的行为一样。当您在 Excel/Sheets 中移动此单元格时,另一个单元格内的公式会重新调整以获取新单元格。我在问这在 Apps 脚本中是否可行。清除 ?
    • @Karim 谢谢你的回复。我不得不为我糟糕的英语水平道歉。不幸的是,我无法理解I want to dynamically assign a value to getRange when the Values change.。可以问一下它的详细情况吗?当我能正确理解它时,我可以寻找解决方案或变通方法。
    • 我的意思是,就像 excel 或 Google Sheet 在将公式从一个单元格移动到另一个单元格时如何保存单元格引用一样,我想知道 getRange 是否也可以具有这种行为并在值更改时自动更改 getRange 值。回顾我的例子,如果我的 A2:B2 值现在是 A3:B3,getRange 值是否可以自动从 A2:B2 更改为 A3:B3 ?所以我的目标是让 getRange 在值更改时自动更改值。
    【解决方案2】:

    这可以通过DeveloperMetadata 实现。元数据可以设置为范围或工作表,每当移动此类数据(范围或工作表)时,相关的元数据也会随之移动。不幸的是,此元数据不能设置为任意范围,而只能设置为单列或单行。例如,使用A2:B2,我们必须将元数据设置为A 列、B 列和第2 行的整体。但是,一旦设置,就不再需要应用程序脚本。 Google 表格会自动跟踪此类数据的移动。

    示例脚本:

    const setDevMetadata_ = (sheetName = 'Sheet1', rng = '2:2', key = 'a2b2') => {
      SpreadsheetApp.getActive()
        .getSheetByName(sheetName)
        .getRange(rng)
        .addDeveloperMetadata(key);
    };
    
    /**
     * @description Set metadata to a specific range
     * Unfortunately, this metadata cannot be set to arbitrary ranges, but only to single column or single row.
     * For eg, with `A2:B2`, We have to set the metadata to the entirety of column A, column B and Row 2.
     * @see https://stackoverflow.com/a/73376887
     */
    const setDevMetadataToA2B2 = () => {
      ['2:2', 'A:A', 'B:B'].forEach((rng) => setDevMetadata_(undefined, rng));
    };
    
    /**
     * @description Get a range with specific developer metadata key
     */
    const getRangeWithKey = (sheetName = 'Sheet1', key = 'a2b2') => {
      const sheet = SpreadsheetApp.getActive().getSheetByName(sheetName),
        devFinder = sheet.createDeveloperMetadataFinder(),
        [rows, columns] = ['Row', 'Column'].map((rc) =>
          devFinder
            .withKey(key)
            .withLocationType(
              SpreadsheetApp.DeveloperMetadataLocationType[rc.toUpperCase()]
            )
            .find()
            .map((devmetadata) =>
              devmetadata.getLocation()[`get${rc}`]()[`get${rc}`]()
            )
        );
    
      console.log({ rows, columns });
      const rng = sheet.getRange(
        rows[0],
        columns[0],
        rows[1] ? rows[1] - rows[0] + 1 : 1,
        columns[1] ? columns[1] - columns[0] + 1 : 1
      );
      console.log(rng.getA1Notation());
      return rng;
    };
    

    【讨论】:

    • 感谢您编辑标题和回复。你似乎明白我的需要。但是您是说使用您的解决方案,不再需要 Apps 脚本。我的问题的目的是让 getRange 值在添加/删除行时自动分配给新值。就像您通过公式移动与另一个单元格有链接的单元格时 Excel/Google 工作表的行为方式一样。 Excel/Sheets 会自动分配给新单元格。这就是我想在 Apps 脚本中复制的内容。
    • @Karim 要获得一个范围,有很多方法。常用的方法是调用.getRange() 并带有注释.getRange('a2:b2')。其他包括行列:如.getRange(2,1,1,2)。在我的示例中,我们使用 developermetadata 键调用:getRangeWithKey('Sheet1', 'a2b2')。为了正确返回范围,应该已经使用setDevMetadataToA2B2() 设置了元数据键。但是,代码中的那些字符串不会自动更改。您也许可以使用 @Tanaike 的代码和 google-apps-script-api 来达到相同的结果,但这很复杂
    • 再次感谢您的回复。据我了解,这并不像看起来那么容易,甚至可能不可行(让 getRange 自动重新调整到新值)。感谢您让我知道和您的时间。当然,我问过另一个人,也许他的意见可以给我更多关于一些替代方案的想法。
    猜你喜欢
    • 2019-05-02
    • 2011-11-06
    • 1970-01-01
    • 1970-01-01
    • 2016-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多