用于在 Google 工作表中查找包含值的最后一行的 ARRAY 公式
ARRAY formula to find last row to contain value in Google Sheets
我有一个 Google Sheet 是通过 Zapier 集成自动填充的。对于添加的每个新行,我需要评估给定的单元格(托运人姓名)以在前面的行中找到托运人姓名的最后一个实例,如果是这样,最后一个条目的 return 行号。
我正在尝试创建一个公式,它只查看新行中的名称和 returns 具有该名称的最新行的编号。
公式需要 运行 作为数组公式,以便数据自动填充添加到 Sheet 的每个新行。
我试过使用这个公式,但是当重构为数组公式时,它不会为新行填充新值,它只是为所有行重复第一个值。
从第 J 行开始:
=sumproduct(max(row(A:A3)*(F4=F:F3)))
我需要将这个公式重构为一个数组公式,自动填充它下面的所有单元格。
我试过这个版本,但是不行:
=ArrayFormula(IF(ISBLANK($A2:$A),"",sumproduct(max(row(A:A3)*($F4:$F=F:F3))))
脚本(也许是自定义函数?)会更好。
解决方案 1
以下是您可以放入 header 的公式(放入 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 行,则编号必须为
13001234
。 001234
是右对齐的部分。
- 对齐是通过将序列号乘以 10 的
(log10(total number of rows) + 1)
次方来完成的,得到 13000000
(来自上面的示例)。这种方法用于避免使用 LEN
和 TEXT
- 使用数字比使用字符串更快。
- 虚拟范围第一列的特殊编号和第二列的原始行号几乎相同。
- 几乎相同的特殊数字:它们只是增加了1,所以
VLOOKUP
最多会在当前字符串对应的数字前停止一步。
- 虚拟范围也有一些特殊的行(在排序前添加在底部),所有
0
作为其特殊数字(第 1 列)的右侧部分,0
用于行号(第二列)。这样做是为了 VLOOKUP
会在条目第一次出现时找到它。
- 虚拟范围已排序,因此我们可以使用外部
VLOOKUP
的 is_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
的原因。
这是一种获取您感兴趣的信息的简单得多的方法。(我认为。)我主要是在猜测您想要什么,因为您的问题并不是真正关于您想要什么,而是关于如何得到你认为会帮助你得到你想要的东西的东西。这是 XY problem 的示例。我试图根据经验猜测你真正想要的是什么。
This editable sheet 仅包含 3 个公式。 2 个关于原始数据 sheet,一个在名为“分析”的新选项卡中。
原始数据选项卡上的第一个公式使用 MMULT 和 SPLIT 函数的组合提取格式正确的时间戳,如下所示:
=ARRAYFORMulA({"Good Timestamp";IF(A2:A="",,MMULT(N(IFERROR(SPLIT(A2:A,"T"))),{1;1}))})
第二个公式计算自该托运人的上一个时间戳以来的时间量。并从当前时间戳中减去它,从而为您提供时间戳之间的时间。但是,它只会在时间少于 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'")
希望我猜对了你的真正目标。始终尽最大努力解释它们是什么,而不是只询问您认为可以帮助您找到答案的一小部分。您最终可能会在不知不觉中使流程过于复杂。
我有一个 Google Sheet 是通过 Zapier 集成自动填充的。对于添加的每个新行,我需要评估给定的单元格(托运人姓名)以在前面的行中找到托运人姓名的最后一个实例,如果是这样,最后一个条目的 return 行号。
我正在尝试创建一个公式,它只查看新行中的名称和 returns 具有该名称的最新行的编号。
公式需要 运行 作为数组公式,以便数据自动填充添加到 Sheet 的每个新行。
我试过使用这个公式,但是当重构为数组公式时,它不会为新行填充新值,它只是为所有行重复第一个值。
从第 J 行开始:
=sumproduct(max(row(A:A3)*(F4=F:F3)))
我需要将这个公式重构为一个数组公式,自动填充它下面的所有单元格。
我试过这个版本,但是不行:
=ArrayFormula(IF(ISBLANK($A2:$A),"",sumproduct(max(row(A:A3)*($F4:$F=F:F3))))
脚本(也许是自定义函数?)会更好。
解决方案 1
以下是您可以放入 header 的公式(放入 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 行,则编号必须为
13001234
。001234
是右对齐的部分。 - 对齐是通过将序列号乘以 10 的
(log10(total number of rows) + 1)
次方来完成的,得到13000000
(来自上面的示例)。这种方法用于避免使用LEN
和TEXT
- 使用数字比使用字符串更快。 - 虚拟范围第一列的特殊编号和第二列的原始行号几乎相同。
- 几乎相同的特殊数字:它们只是增加了1,所以
VLOOKUP
最多会在当前字符串对应的数字前停止一步。 - 虚拟范围也有一些特殊的行(在排序前添加在底部),所有
0
作为其特殊数字(第 1 列)的右侧部分,0
用于行号(第二列)。这样做是为了VLOOKUP
会在条目第一次出现时找到它。 - 虚拟范围已排序,因此我们可以使用外部
VLOOKUP
的is_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
的原因。
这是一种获取您感兴趣的信息的简单得多的方法。(我认为。)我主要是在猜测您想要什么,因为您的问题并不是真正关于您想要什么,而是关于如何得到你认为会帮助你得到你想要的东西的东西。这是 XY problem 的示例。我试图根据经验猜测你真正想要的是什么。
This editable sheet 仅包含 3 个公式。 2 个关于原始数据 sheet,一个在名为“分析”的新选项卡中。
原始数据选项卡上的第一个公式使用 MMULT 和 SPLIT 函数的组合提取格式正确的时间戳,如下所示:
=ARRAYFORMulA({"Good Timestamp";IF(A2:A="",,MMULT(N(IFERROR(SPLIT(A2:A,"T"))),{1;1}))})
第二个公式计算自该托运人的上一个时间戳以来的时间量。并从当前时间戳中减去它,从而为您提供时间戳之间的时间。但是,它只会在时间少于 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'")
希望我猜对了你的真正目标。始终尽最大努力解释它们是什么,而不是只询问您认为可以帮助您找到答案的一小部分。您最终可能会在不知不觉中使流程过于复杂。