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)
);
}