A script (custom function maybe?) would be better.
Solution 1
Below is a formula you can place into the header (put in in J1
, remove everything below).
It works much faster than the second solution and has no N² size restriction. Also it works with empty shippers (& "♥"
is for those empty ones): as long as A:A
column has some value it will not be ignored.
={
"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
)
)
)
}
Details on how it works
- For every row we use
VLOOKUP
to search for a special number in a sorted virtual range to get the row number of the previous entry matching current.
- A special number for a row is constructed like this: we get a sequential number for the current entry among unique entries and append to it current row number.
- The right part (row number) of the resulting special numbers must be aligned between them. If the entry has sequential number 13 and the row number is 1234 and there are 100500 rows, then the number must be
13001234
. 001234
is the aligned right part.
- Alignment is done by multiplying a sequential number by 10 to the power of
(log10(total number of rows) + 1)
, gives us 13000000
(from the example above). This approach is used to avoid using LEN
and TEXT
- working with numbers is faster then working with strings.
- Virtual range has almost the same special numbers in the first column and original row numbers in the second.
- Almost the same special numbers: they just increased by 1, so
VLOOKUP
will stop at most one step before the number corresponding to the current string.
- Also virtual range has some special rows (added at the bottom before sorting) which have all
0
's as the right part of their special numbers (1st column) and 0
for the row number (2nd column). That is done so VLOOKUP
will find it for the first occurrence of the entry.
- Virtual range is sorted, so we could use
is_sorted
parameter of the outer VLOOKUP
set to 1: that will result in the last match that is less or equal to the number being looked for.
& "♥"
are appended to the entries, so that empty entries also will be found by VLOOKUP
.
Solution 2 - slow and has restrictions
But for some small enough number of rows this formula works (put in in J1
, remove everything below):
={
"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*$"
)
)
}
But there is a problem. The virtual range inside of the formula is of size N², where N is the number of rows. For current 1253 rows it works. But there is a limit after which it will throw an error of a range being too large.
That is the reason to use FILTER(...)
and not just F2:F
.