ArrayFormula is breaking the getLastRow() funtion. Possible workarounds?

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.


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

Leave a Comment