Issue:
- Undesirable addition of empty strings in all the available rows by traditional usage of
ARRAYFORMULA(IF(A:A="",...))
Solution:
Using ARRAYFORMULA
properly with INDEX/COUNTA
(to determine the last row that's needed) ensures formula is only filled upto the needed row instead of a camouflage
INDEX/COUNTA
: INDEX
returns a value as well as a cell reference. A2:INDEX(A2:A,COUNTA(A2:A))
=> If COUNTA(...)
returns 10 => A2:INDEX(A2:A,10)
=> A2:A11
is the final reference feeded to weeknum
Assuming there are no blanks in between your data,
=ARRAYFORMULA(WEEKNUM(A2:INDEX(A2:A,COUNTA(A2:A)),2))
Another alternative is to use ARRAY_CONSTRAIN/COUNTA
:
=ARRAY_CONSTRAIN(ARRAYFORMULA(WEEKNUM(A2:A, 2)),COUNTA(A2:A))
The usage of COUNTA
assumes there are no blank cells in between. If there are any, you may need to manually add a offset. If there are two blank cells, add 2 to COUNTA
A2:INDEX(A2:A,COUNTA(A2:A)+2)
Unless Google does inbuilt optimizations, INDEX/COUNTA
is preferred over ARRAY_CONSTRAIN
.
Update:
With the adevent of XMATCH
and named functions, this should become easier. Create a named function like:
LR(rng)
(LAST ROW):
=INDEX(rng,XMATCH("*",rng,2,-1))
LRA(rng, blanks)
(LASTROW ADVANCED):
=INDEX(rng,COUNTA(rng)+blanks)
XMATCH
does the search in reverse and in theory, should be faster. But the wildcard *
search won't consider numbers/dates. So, if the last row is a number or a date, we need to use COUNTA
. You can then use it like this:
=ARRAYFORMULA(WEEKNUM(A2:LRA(A:A,),2))
If there are like 5 intervening blanks, use
=ARRAYFORMULA(WEEKNUM(A2:LRA(A:A,5),2))
google-apps-script
It might be hard to fix those array formulas with INDEX/COUNTA
manually, so I made a script. This is just a proof of concept and alpha quality. So, test it in a copy of your spreadsheet rather than on the original. Having said that, I'm sure it'll handle most common cases without trouble.
/**
* @see https://stackoverflow.com/a/46884012
*/
function fixArrayFormulas_so46884012() {
const ss = SpreadsheetApp.getActive()/*.getSheetByName('Sheet1')*/,
map = new Map([
[
// Normalize first part of range
/* A:F */ String.raw`([a-z]+):([a-z]+)`,
/* A1:F*/ String.raw`$11:$2`,
],
[
// Convert any previous index/counta to normal ranges
/* A1:INDEX(F:F,COUNTA(F:F)) */ String.raw`([a-z]+\d+):INDEX\(([a-z]+)\d*:\w+,COUNTA\(\w+:\w+\)\)`,
/*A1:F*/ String.raw`$1:$2`,
],
[
// Convert open ended ranges to index/counta ranges
/*A1:F*/ String.raw`([a-z]+\d+:)([a-z]+)`,
/* A1:INDEX(F:F,COUNTA(F:F)) */ `$1INDEX($2:$2,COUNTA($2:$2))`,
],
]);
map.forEach((v, k) =>
ss
.createTextFinder(k)
.matchFormulaText(true)
.useRegularExpression(true)
.replaceAllWith(v)
);
}