Forum Discussion

leolapa's avatar
leolapa
Brass Contributor
Sep 03, 2021
Solved

Find last MATCH for each row on a 2D running array with ONE "spill" formula

Below is a short snippet of a particular column with a list of stock tickers, which is part of a huge multi-column table:

 

In order to get to a 2D array of that column, accumulating the rows as the column progresses down, I went through the usual procedure through the formula

 

=IF(--(SEQUENCE(;ROWS($A$2:$A$21);0)<SEQUENCE(ROWS($A$2:$A$21)))=1;TRANSPOSE($A$2:$A$21);"")

 

which produced the yellow shaded array pictured below:

 

Then from the above array I need to extract the ROW number of each last MATCH out of the TRANSPOSED/SORT/UNIQUE list of tickers first pictured UP above on the first image.

 

=TRANSPOSE(SORT(UNIQUE($A$2:$A$21)))

 

If I were to do that by individual cell, then we're talking about a pretty basic solution, such as this

 

=MAX(SEQUENCE(;COLUMNS($C2:$V2))*--($C2:$V2=X$1))

 

and then just drag that down and across a 20 ROWS by 9 COLUMNS array to get the job done.

 

However, that solution won't work for me, as I'm embedding that onto another much more complex dynamic formula that is supposed to automatically pick up all new rows and tickers that will come up in the future, since we're talking about a live growing table, and I don't want to worry about checking the formula span every time I update the input schedule.

 

Since the Excel team recently released a few great functions that work well with one-dimensional dynamic arrays, I made use of the BYROW function in order to get that individual-cell solution and turn it into a dynamic solution that "spills" down each correspondent ticker column as the source list grows in rows

 

=BYROW($C$2#;LAMBDA(Ultimo_Instrum_Por_Linha;MAX(IF(Ultimo_Instrum_Por_Linha=X$1;COLUMN(Ultimo_Instrum_Por_Linha)-MIN(COLUMN(Ultimo_Instrum_Por_Linha))+1))))

 

and that produced me the produced the green shaded array that follows:

 

But then there's another catch: please note that the formula works for each individual ticker, however every time a new ticker comes up on an additional column further to the right of the spreadsheet, the formula won't automatically pick that up.

 

And remember that this formula will go into another formula, so dragging it across additional "placeholder" columns won't do the job since this won't even be sitting anywhere!

 

So, as the post title says, I need a 2-dimensional formula for that goal: in other words, a formula that will "spill" the results down the ROWS and across the COLUMNS.

 

Unfortunately, an Excel native "BY2DARRAY" function hasn't been created yet for that purpose, and that's the workaround I need to get to in order to crack that nut.

 

I'm currently working on a combination of BYROW and BYCOL via LET, but still having a hard time to reach a final solution...

 

Any ideas?!

 

P.S.:

Please find attached the actual workbook file.

  • leolapa  I'm not quite sure if you're looking to replicate the full green area, or just the last row, but if just the last row, how about this?

     

    =LET(
        ticker_list,$A$2:$A$21,
        uniq,TRANSPOSE(SORT(UNIQUE(ticker_list))),
        rows_,SEQUENCE(ROWS(ticker_list)),
        MAP(uniq,LAMBDA(u,MAX(FILTER(rows_,ticker_list=u))))
    )

     

10 Replies

  • leolapa's avatar
    leolapa
    Brass Contributor

    Thanks tboulden and PeterBartholomew1 for the great solution alternatives.

     

    I also came up with my own, albeit much longer and therefore not near as elegant as the ones you guys came up with:

     

    =INDEX(BYROW(--(INDEX(IF(--(SEQUENCE(;ROWS($A$2:$A$21);0)<SEQUENCE(ROWS($A$2:$A$21)))=1;TRANSPOSE($A$2:$A$21);"");SEQUENCE(ROWS($A$2:$A$21)*ROWS(UNIQUE($A$2:$A$21));ROWS($A$2:$A$21);;0)*SEQUENCE(ROWS($A$2:$A$21)*ROWS(UNIQUE($A$2:$A$21)))-(TRUNC(SEQUENCE(ROWS($A$2:$A$21)*ROWS(UNIQUE($A$2:$A$21));;0)/ROWS($A$2:$A$21))*ROWS($A$2:$A$21));SEQUENCE(ROWS($A$2:$A$21)*ROWS(UNIQUE($A$2:$A$21));ROWS($A$2:$A$21);;0)*SEQUENCE(;ROWS($A$2:$A$21)))=INDEX(SORT(UNIQUE($A$2:$A$21));ROUNDUP(SEQUENCE(ROWS(UNIQUE($A$2:$A$21))*ROWS($A$2:$A$21))/ROWS($A$2:$A$21);0)))*(SEQUENCE(ROWS($A$2:$A$21)*ROWS(UNIQUE($A$2:$A$21));ROWS($A$2:$A$21);;0)*SEQUENCE(;ROWS($A$2:$A$21)));LAMBDA(Last_Match_By_Row;MAX(Last_Match_By_Row)));TRANSPOSE(SEQUENCE(ROWS(UNIQUE($A$2:$A$21));ROWS($A$2:$A$21))))

     

     I'll definitely go with one of you guys' since either approach will make for a much leaner calculation mechanism, and avoid taking my workbook to a potential slow performance issue down the road...

     

    SergeiBaklan for now I think a formulaic approach ought get the job I need done, but I sure appreciate your valuable input and the aforementioned code will sure help me on a future occasion.

     

    P.S.:

    I marked tboulden's as the best answer as I don't know another way to close this query as answered, but for sure any of the 3 proposed approaches are great.

  • leolapa 

    I think I have managed to reproduce your green table using MAP over 2D

     

    = LET(
       k, SEQUENCE(ROWS(StockTicker)),
       target, Broadcastλ(ticker#, k),
       end, Broadcastλ(k,ticker#),
       MAP(target,end, LastMatchλ))

     

    where LastMatchλ searches part of the StockTicker column for a specific ticker

     

    "LastMatchλ"
    = LAMBDA(t,e,
        LET(
          rng, INDEX(StockTicker,1):INDEX(StockTicker,e-1),
          IF(e>1, IFERROR(XMATCH(t,rng,,-1),0),0)
        )
      )

     

    and Broadcastλ merely broadcasts a vector to form an array

     

    "Broadcastλ"
    = LAMBDA(x,y,
        IF(LEN(y),x)
      )

     

    In doing so, I have removed the yellow helper range.

      • PeterBartholomew1's avatar
        PeterBartholomew1
        Silver Contributor

        tboulden 

        True.  By going up to, but not including, the current row, the final row is never returned and the first row of the result table is blank.  I was simply trying to replicate a set of numbers with little or no insight as to the purpose of the calculation!

         

        Added:

        Just taken a look at the screen shot in the OP.  Seems the adjustment is not needed.  Thanks.

  • tboulden's avatar
    tboulden
    Iron Contributor

    leolapaIf you're looking for the full green area, try this.

    =LET(
        ticker_list,$A$2:$A$21,
        uniq,TRANSPOSE(SORT(UNIQUE(ticker_list))),
        rows_,SEQUENCE(ROWS(ticker_list)),
        match_rows,--(ticker_list=uniq)*rows_,
        MAKEARRAY(
            ROWS(match_rows),
            COLUMNS(match_rows),
            LAMBDA(i,j,
                IF(
                    i=1,INDEX(match_rows,i,j),
                   MAX(
                        INDEX(match_rows,SEQUENCE(i-1),j),
                        INDEX(match_rows,i,j)
                    )
                )
            )
        )
    )
    • tboulden's avatar
      tboulden
      Iron Contributor

      Simplified for extraneous calc:

      =LET(
          ticker_list,$A$2:$A$21,
          uniq,TRANSPOSE(SORT(UNIQUE(ticker_list))),
          rows_,SEQUENCE(ROWS(ticker_list)),
          match_rows,--(ticker_list=uniq)*rows_,
          MAKEARRAY(
              ROWS(match_rows),
              COLUMNS(match_rows),
              LAMBDA(i,j,
                  IF(
                      i=1,INDEX(match_rows,i,j),
                      MAX(
                          INDEX(match_rows,SEQUENCE(i),j)
                      )
                  )
              )
          )
      )

       

       

  • tboulden's avatar
    tboulden
    Iron Contributor

    leolapa  I'm not quite sure if you're looking to replicate the full green area, or just the last row, but if just the last row, how about this?

     

    =LET(
        ticker_list,$A$2:$A$21,
        uniq,TRANSPOSE(SORT(UNIQUE(ticker_list))),
        rows_,SEQUENCE(ROWS(ticker_list)),
        MAP(uniq,LAMBDA(u,MAX(FILTER(rows_,ticker_list=u))))
    )

     

Resources