ArrayFormula 其中 returns 倒数第二个匹配

ArrayFormula which returns the second last matching

我有一个 table 收集许多不同玩家的 总分 的每日读数。由于是手动采集,可能有的玩家每天会不止一次添加阅读量,也有可能一天甚至更长时间根本没有阅读量。 该结构是非常基本的 3 列(日期、玩家、总计)。

我正在寻找一个 ArrayFormula 可以自动将特定玩家的 每日得分 填入第 4 列。这可以通过一个公式来实现,该公式找到特定玩家的倒数第二个读数并将其从 last/current 读数中减去。

Date Player Total Daily
17/10/2021 Player 001 1500 1500
17/10/2021 Player 007 700 700
19/10/2021 Player 003 700 700
19/10/2021 Player 005 100 100
19/10/2021 Player 004 1100 1100
19/10/2021 Player 006 300 300
19/10/2021 Player 002 900 900
20/10/2021 Player 006 900 600
20/10/2021 Player 006 1600 700
20/10/2021 Player 002 1100 200
20/10/2021 Player 005 600 500
20/10/2021 Player 009 200 200
21/10/2021 Player 001 1600 100
21/10/2021 Player 003 1000 300

我找到了一个非常有趣的解决方案,但由于它基于 INDIRECT,因此无法与 ArrayFormula 一起使用: https://infoinspired.com/google-docs/spreadsheet/find-the-last-matching-value-in-google-sheets/

我想到了一种不同的方法,使用 VLOOKUP 并将搜索范围限制为当前行上方的行,然后找到该范围内的最后一个匹配值(-实际上是第二个-最后 table),但我找不到适用于 ArrayFormula 的语法。

有什么想法吗?

我将提供一个暂定的解决方案,但要知道在无法查看一些实际数据和预期结果的情况下编写这样的公式总是很困难。

假设您的数据在 A2:C 中(headers 在 A1:C1 中)。在原本为空的 Col D 的 D2 中尝试以下公式:

=ArrayFormula(IF(A2:A="",,C2:C - (VLOOKUP(B2:B&(A2:A-1), SORT({ {"", 0}; {B2:B&A2:A, C2:C} }), 2, TRUE) * (VLOOKUP(B2:B&(A2:A-1), SORT({ {"", 0}; {B2:B&A2:A, B2:B} }), 2, TRUE) = B2:B))))

要找到每位玩家的 second-to-last 得分,VLOOKUP 会在包含 A 的 SORTed 虚拟范围内查找每一行的 player-and-“昨天”的串联.) {null, 0} 在 B 之上。) {每行的 player-and-date, score 的串联}.

因为SORT,可以使用最后一个参数TRUE,这意味着如果找不到player-and-“昨天”的精确匹配,最接近的之前的匹配将被返回。 * VLOOKUP(...) 是为了确保之前的匹配是同一个人(因为每个人最早日期之前的字母顺序条目将是其他人的 last 日期,除了按字母顺序排列的第一人称,谁将弹回 {null, 0})。

但是,如果您的 sheet 数据下方始终至少有一个空白行,您可以稍微简化一下:

=ArrayFormula(IF(A2:A="",,C2:C - (VLOOKUP(B2:B&(A2:A-1), SORT({B2:B&A2:A, C2:C}), 2, TRUE) * (VLOOKUP(B2:B&(A2:A-1), SORT({B2:B&A2:A, B2:B}), 2, TRUE) = B2:B))))

这是因为bounce-back对于第一个字母的人的第一次约会,会为所有空行查找{null, null},相当于{null, 0},都将SORTed 早于你所有的数据。所以我们不需要将它包含在虚拟阵列设置中。

如果结果不符合预期,请分享符合预期结果的最小真实数据集。

ADDENDUM根据 OP 的附加评论)

如果一个玩家每天可以输入多个分数,您可以使用下面的公式版本。

如果您不确定,您的数据下方至少会有一个空白行:

=ArrayFormula(IF(A2:A="",,C2:C - (VLOOKUP(B2:B&TEXT(ROW(B2:B)-1,"0000"), SORT({ {"", 0}; {B2:B&TEXT(ROW(B2:B),"0000"), C2:C} }), 2, TRUE) * (VLOOKUP(B2:B&TEXT(ROW(B2:B)-1,"0000"), SORT({ {"", 0}; {B2:B&TEXT(ROW(B2:B),"0000"), B2:B} }), 2, TRUE) = B2:B))))

如果您确定您的数据下方始终至少有一个空白行:

=ArrayFormula(IF(A2:A="",,C2:C - (VLOOKUP(B2:B&TEXT(ROW(B2:B)-1,"0000"), SORT( {B2:B&TEXT(ROW(B2:B),"0000"), C2:C} ), 2, TRUE) * (VLOOKUP(B2:B&TEXT(ROW(B2:B)-1,"0000"), SORT( {B2:B&TEXT(ROW(B2:B),"0000"), B2:B} ), 2, TRUE) = B2:B))))

以上两个都用行号代替日期。然后,他们假设您的数据将始终按照实时发生的顺序输入,而不是随机输入(即,您不会在较晚日期的分数之后输入较早日期的分数)。如果您 可能输入乱序的东西,这也可以控制;但是我这里没有这样做。

试试这个:

=ARRAYFORMULA(
  IF(
    A2:A = "",,
        C2:C
      - IFNA(VLOOKUP(
            MATCH(
              B2:B,
              UNIQUE(FILTER(B2:B, B2:B <> "")),
            )
          * 10^INT(LOG10(ROWS(A2:A)) + 1)
          + ROW(A2:A) - 1,
          SORT(
            {
              SEQUENCE(COUNTUNIQUE(B2:B)) * {10^INT(LOG10(ROWS(A2:A)) + 1), 0};
              FILTER(
                {
                    MATCH(
                      B2:B,
                      UNIQUE(FILTER(B2:B, B2:B <> "")),
                    )
                  * 10^INT(LOG10(ROWS(A2:A)) + 1)
                  + ROW(A2:A),
                  C2:C
                },
                A2:A <> ""
              )
            },
            1, 1
          ),
          2
        ))
  )
)