google-sheetssumflattencumulative-sumsumifs

Using ARRAYFORMULA to Calculate Running Total of Payables (Alternative to INDIRECT)


I use a Google Spreadsheet to keep track of the accounts payable per vendor. There is a sheet per vendor in the Spreadsheet. A simplified sheet looks like this:

A Vendor Sheet

When I receive a new invoice, an entry for the amount is made in the Credit column and when I release a payment, an entry for the amount is made in the Debit column. I keep track of the running total in the AC Payable column. I achieve this by using a formula in each cell of the AC Payable column (the example below is from cell E4):

=IF(
  ISNUMBER(INDIRECT(ADDRESS(ROW()-1,COLUMN()))),
  INDIRECT(ADDRESS(ROW()-1,COLUMN()))+C4-D4,
  C4-D4
)

The logic is simple. The running total for row n is calculated by:

AC Payable(n - 1) + Credit(n) - Debit(n)

This setup works fine, except I have to drag the formula into newly added rows. Is there a way to achieve this by using ARRAYFORMULA?

PS: I have found a solution using:

= ARRAYFORMULA(
    SUMIF(
      ROW(C3:C),
      "<="&ROW(C3:C),
      C3:C) 
    - 
    SUMIF(
      ROW(D3:D),
      "<="&ROW(D3:D),
      D3:D
    )
  )

I feel this is a suboptimal (The original sheet dates back to 2018. It has a lot of rows) solution since, in every row, it calculates the total of the Debit and Credit columns up to the current row and then subtracts the total of the Debit column from the total of the Credit column.

I am expecting a solution that would take advantage of the running total available in the previous row and not redo the whole calculation per row.


Solution

  • solution for up to 1581 rows:

    =ARRAYFORMULA(QUERY(QUERY(MMULT(TRANSPOSE((SEQUENCE(COUNTA(A3:A)*2)<=
     SEQUENCE(1, COUNTA(A3:A)*2))*FLATTEN(INDIRECT("C3:D"&COUNTA(A3:A)+ROW(A3)-1)*{1, -1})), 
     SEQUENCE(COUNTA(A3:A)*2, 1, 1, 0)), "offset 1", ), "skipping 2", ))
    

    enter image description here

    skills:


    it's based on standard MMULT Running/Cumulative Total/Sum formula:

    =ARRAYFORMULA(MMULT(TRANSPOSE((ROW(B1:B6) 
     <=TRANSPOSE(ROW(B1:B6)))*B1:B6), SIGN(B1:B6)))
    

    but with a modification twist, because you got 2 columns to total

    instead of ROW(B1:B6) we use a sequence of count of real data multiplied by two (because you got 2 columns):

    SEQUENCE(COUNTA(A3:A)*2)
    

    instead of TRANSPOSE(ROW(B1:B6)) we use again:

    SEQUENCE(1, COUNTA(A3:A)*2)
    

    combination of these pieces:

    =ARRAYFORMULA(TRANSPOSE((SEQUENCE(COUNTA(A3:A)*2)<=SEQUENCE(1, COUNTA(A3:A)*2))))
    

    will produce a matrix like:

    enter image description here

    and that's the reason why it dies with lots of rows because while you may think that if you have only 1500 rows in two columns, then formula will work only on 1500*2=3000 virtual cells, but in fact the MMULT formula processes (1500*2)*(1500*2)=9000000 virtual cells. still, it's worth to note, that this MMULT fx is great if deployed on a small scale.

    next, instead of *B1:B6 we use:

    *FLATTEN(INDIRECT("C3:D"&COUNTA(A3:A)+ROW(A3)-1)*{1, -1}))
    

    eg. with INDIRECT we take only "valid" range of C3:D which is in your example sheet just C3:D5 and we multiply C column by 1 and D column by -1 to simulate subtraction and then we FLATTEN both columns into one single column. the part +ROW(A3)-1 is just an offset because you start from row 3

    and the last part of standard RT fx - SIGN(B1:B6) is replaced with one column full of ones:

    SEQUENCE(COUNTA(A3:A)*2, 1, 1, 0)
    

    then we offset the output with inner QUERY by 1 because we are interested in a totals after subtraction and finally we use skipping 2 which means that we filter out every second value - again, we are interested in totals after subtraction of D column.


    solution for more than 1581 rows:

    =ARRAYFORMULA(
     SUMIF(SEQUENCE(COUNTA(A3:A)), "<="&SEQUENCE(COUNTA(A3:A)), INDIRECT("C3:C"&COUNTA(A3:A)))-
     SUMIF(SEQUENCE(COUNTA(A3:A)), "<="&SEQUENCE(COUNTA(A3:A)), INDIRECT("D3:D"&COUNTA(A3:A))))
    

    enter image description here

    skills: