【问题标题】:ARRAY formula to find last row to contain value in Google SheetsARRAY 公式查找最后一行以包含 Google 表格中的值
【发布时间】:2021-02-02 07:47:53
【问题描述】:

我有一个通过 Zapier 集成自动填充的 Google 表格。对于添加的每个新行,我需要评估给定的单元格(发货人名称)以在前几行中查找发货人名称的最后一个实例,如果是,则返回最后一个条目的行#。

Example Data Sheet

我正在尝试创建一个公式,它只查看新行中的名称并返回具有该名称的最近行的编号。

公式需要作为数组公式运行,以便数据自动填充添加到工作表的每个新行。

我尝试使用这个公式,但是当重构为数组公式时,它不会为新行填充新值,它只是为所有行重复第一个值。

从 J 行开始:

=sumproduct(max(row(A$1:A3)*(F4=F$1:F3)))

我需要将此公式重构为自动填充其下方所有单元格的数组公式。

这个版本我试过了,还是不行:

=ArrayFormula(IF(ISBLANK($A2:$A),"",sumproduct(max(row(A$1:A3)*($F4:$F=F$1:F3))))

【问题讨论】:

  • 您愿意考虑使用 Apps 脚本方法来解决您的问题吗? @PaulWeinstein
  • @ale13 - 是的!我也走上了这条路,但是因为我不懂 Javascript,所以我没有走得太远。我确实认为对于开发人员来说,这是一个简单的任务,使用循环脚本来简单地找到上面下一个单元格的行。如果有一个自定义函数也能做到这一点,我将不胜感激。
  • 问题应该是自包含的。虽然欢迎使用外部链接,但请考虑添加一个 ascii 表,如(thisthis)或屏幕截图或 csv 文本(如 this)以显示您的数据结构。
  • @PaulWeinstein 似乎您想要这些行号,以便可以在其他公式中使用它们。您是否对不需要使用您要询问的“最后”行号的其他列信息的更简单的解决方案感兴趣?

标签: google-sheets array-formulas


【解决方案1】:

脚本(可能是自定义函数?)会更好。

解决方案 1

以下是您可以放入标题的公式(放入J1,删除以下所有内容)。

它的运行速度比第二种解决方案快得多,并且没有 N² 大小限制。它也适用于空托运人(& "♥" 用于那些空托运人):只要 A:A 列具有某些值,它就不会被忽略。

={
  "Row of Last Entry";
  ARRAYFORMULA(
    IF(
      A2:A = "",
        "",
        VLOOKUP(
            ROW(F2:F)
          + VLOOKUP(
              F2:F & "♥", 
              {
                UNIQUE(F2:F & "♥"),
                  SEQUENCE(ROWS(UNIQUE(F2:F)))
                * POWER(10, INT(LOG10(ROWS(F:F))) + 1)
              },
              2,
              0
            ),
          SORT(
            {
                ROW(F2:F) + 1
              + VLOOKUP(
                  F2:F & "♥", 
                  {
                    UNIQUE(F2:F & "♥"),
                      SEQUENCE(ROWS(UNIQUE(F2:F)))
                    * POWER(10, INT(LOG10(ROWS(F:F))) + 1)
                  },
                  2,
                  0
                ),
              ROW(F2:F);
              {
                  SEQUENCE(ROWS(UNIQUE(F2:F)))
                * POWER(10, INT(LOG10(ROWS(F:F))) + 1),
                SEQUENCE(ROWS(UNIQUE(F2:F)), 1, 0, 0)
              }
            },
            1,
            1
          ),
          2,
          1
        )
    )
  )
}

详细说明其工作原理

  • 对于每一行,我们使用VLOOKUP 在已排序的虚拟范围内搜索特殊数字,以获取与当前匹配的上一个条目的行号。
  • 行的特殊编号的构造如下:我们在唯一条目中获取当前条目的序号,并将当前行号附加到该序号。
  • 生成的特殊数字的右侧部分(行号)必须在它们之间对齐。如果条目的序号为 13,行号为 1234 且有 100500 行,则编号必须为 13001234001234 是右对齐部分。
  • 对齐是通过将序列号乘以 10 的 (log10(total number of rows) + 1) 的幂来完成的,得到 13000000(来自上面的示例)。此方法用于避免使用 LENTEXT - 处理数字比处理字符串更快。
  • 虚拟范围在第一列中具有几乎相同的特殊数字,在第二列中具有原始行号。
  • 几乎相同的特殊数字:它们只是增加了 1,因此VLOOKUP 最多会在当前字符串对应的数字前一步停止。
  • 虚拟范围还有一些特殊的行(在排序前添加到底部),其中所有 0 的右侧部分作为其特殊编号(第 1 列),0 作为行号(第 2 列) .这样做是为了让VLOOKUP 在第一次出现该条目时找到它。
  • 虚拟范围已排序,因此我们可以使用外部 VLOOKUPis_sorted 参数设置为 1:这将导致最后一个匹配小于或等于正在查找的数字。
  • & "♥" 被附加到条目后,因此VLOOKUP 也将找到空条目。

解决方案 2 - 速度慢且有限制

但是对于一些足够少的行,这个公式有效(输入J1,删除下面的所有内容):

={
  "Row of Last Entry";
  ARRAYFORMULA(
    REGEXEXTRACT(
      TRANSPOSE(QUERY(TRANSPOSE(
        IF(
            (FILTER(ROW(F2:F), F2:F <> "") > TRANSPOSE(FILTER(ROW(F2:F), F2:F <> "")))
          * (FILTER(F2:F, F2:F <> "") = TRANSPOSE(FILTER(F2:F, F2:F <> ""))),
            TRANSPOSE(FILTER(ROW(F2:F), F2:F <> "")),
            ""
         )
      ), "", ROWS(FILTER(F2:F, F2:F <> "")))),
      "(\d*)\s*$"
    )
  )
}

但是有一个问题。公式内部的虚拟范围大小为 N²,其中 N 是行数。对于当前的 1253 行,它可以工作。但是有一个限制,超过这个限制就会抛出范围过大的错误。

这就是使用FILTER(...) 而不仅仅是F2:F 的原因。

【讨论】:

  • 到目前为止,这在数据集上完美运行。澄清一点,您使用 FILTER(...) 而不仅仅是 F2:F 的解决方案将防止错误范围过大?
  • 它现在有帮助,因为大约有 17000 多行数据(工作正常)和总共 30000 多行(不起作用)。如果包含数据的行数越来越多,它最终会中断。
  • 我们能否改进公式,使其回溯最大行数? 10000 行回顾对于这个应用程序来说已经足够了。
  • @PaulWeinstein 不确定是否要改进,但我想出了另一个解决方案。它没有前一个那样的限制。
  • @TheMaster 添加了详细信息。
【解决方案2】:

这是获取您感兴趣的信息的一种非常简单的方法。(我认为。)我主要是在猜测您想要什么,因为您的问题并不是关于您想要什么,而是关于如何得到你认为会帮助你得到你想要的东西。这是XY problem 的示例。我试图根据你真正追求的经验来猜测。

This editable sheet 仅包含 3 个公式。 2 在原始数据表上,一个在名为“分析”的新选项卡中。

原始数据选项卡上的第一个公式使用 MMULT 和 SPLIT 函数的组合提取格式正确的时间戳,如下所示:

=ARRAYFORMulA({"Good Timestamp";IF(A2:A="",,MMULT(N(IFERROR(SPLIT(A2:A,"T"))),{1;1}))})

第二个公式计算自该 Shipper 的上一个时间戳以来的时间量。并从当前时间戳中减去它,从而为您提供时间戳之间的时间。但是,只有在时间少于 200 分钟时才会这样做。如果超过 200 分钟,则假定该托运人的班次不同。它看起来像这样,并使用 LOOKUP() 和 SUBSTITUTE() 的组合来确保它正在提取正确的时间戳。显然,如果有意义,您可以找到 200 值并将其更改为更合适的值。

=ARRAYFORMULA({"Minutes/Order";IF(A2:A="",,IF(IFERROR((G2:G-1*SUBSTITUTE(LOOKUP(F2:F&G2:G-0.00001,SORT(F2:F&G2:G)),F2:F,""))*24*60)>200,,IFERROR((G2:G-1*SUBSTITUTE(LOOKUP(F2:F&G2:G-0.00001,SORT(F2:F&G2:G)),F2:F,""))*(24*60))))})

第三个公式,在名为分析的选项卡上,使用查询来显示每个订单的平均分钟数和每个托运人每小时处理的订单数。它看起来像这样:

=QUERY({'Sample Data'!F:I},"Select Col1,AVG(Col3),COUNT(Col3)/(SUM(Col3)/60) where Col3 is not null group by Col1 label COUNT(Col3)/(SUM(Col3)/60)'Orders/ hour',AVG(Col3)'Minutes/ Order'")

希望我猜对了你的真正目标。始终尽力解释它们是什么,而不是只要求您认为可以帮助您找到答案的一小部分。您最终可能会在不知不觉中使您的过程过于复杂。

【讨论】:

  • 你肯定猜对了。谢谢你的解决方案。这完全符合要求。感谢您指导如何提出更好的问题。我会在以后的问题中记住这一点。
猜你喜欢
  • 1970-01-01
  • 2020-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-16
  • 2019-12-12
  • 1970-01-01
相关资源
最近更新 更多