Is there an equivalent of OFFSET for array contents - excel-formula

I would like to dynamically modify the dimensions of array contents just like OFFSET's height and width do with ranges.
Let A1:A5 equal to 1, 2, 3, 4, 5
if I want to get the first 3 values with offset (using a range):
=OFFSET(A1:A5,,3,1) => {1;2;3}
But this doesn't work:
=OFFSET({1;2;3;4;5},,,3,1)
Is there an equivalent to OFFSET but for array contents? This is of course part of a much bigger array formula which forces me to work with array contents instead of ranges.
Of course I could easily do it with VBA but I'd rather not, let's call this company limitations...
Thank you very much for your help.
PS: first time posting here, any remarks/advice to improve my questions are most welcome.

Hi & Welcome to the forum. I think you are almost correct - the only thing you need to do is to press Ctrl+Shift+Enter in order to turn your OFFSET formula into an Array formula:
In the example below I have combined your OFFSET with SUMPRODUCT formula (which works with ranges and arrays). As you can see the result of SUMPRODUCT function is correct (i.e. sum of first 3 values):
Let me know if this is what you were looking for.

You can use INDEX to return an array:
INDEX({1;2;3;4;5},N(IF({1},ROW(INDEX($ZZ:$ZZ,1):INDEX($ZZ:$ZZ,3)))))
This will force any formula that uses this to become an array formula and as such must be entered with Ctrl-Shift-Enter instead of Enter when exiting edit mode.
Change the ,1 to the start position desired and the 3 to the end position desired.
N(IF({1},ROW(INDEX($ZZ:$ZZ,1):INDEX($ZZ:$ZZ,3))))
Creates an array of numbers from the 1 to the 3 in this case. Which is passed to the INDEX and the INDEX will then return an array of the array {1,2,3}
As you can see with this sum of that array:

Related

Get element from array constant?

If you have a CSE array constant as follows in cell A1:
{={2,4,6,8}}
How can you get a specific element from the array constant? I tried the following formulas but they all return the first value of the array constant (2).
=INDEX(A1, 0)
=INDEX(A1, 1)
However, it does work if the array is not a reference. The following formula returns the 3rd element (6).
=INDEX({2,4,6,8},3)
Thank you
You could put the array constant in a Name instead of a cell.
Then INDEX will work with it properly with no implicit intersection.
Or you could parse the formula using the FORMULATEXT function, but that sounds tedious.
Try =INDEX(A1#,1). The # tells excel that A1 is a spill formula (more than 1 cell long). The array index starts at 1, not 0 in this case.
As a side note, Index knows you are referring to an index, not a row, when you give it a 1D array. In your example =INDEX(A1#,4) and =INDEX(A1#,1,4) return the Fourth item in your array (8 in this case), but =INDEX(A1#,4,1) will give you the error #REF!. If you define your array vertically {={2;4;6;8}}, =INDEX(A1#,4) and =INDEX(A1#,4,1) both work.
Edit: It looks like this does not always work in Excel 365 when a an array formula is created using Ctrl+Shift+Enter. I think this is due to the changes in 365. Array formulas have pretty much been replaced with spill formulas. Entering ={2,4,6,8} is mostly equivalent to a pre-365 array formula, but creating an array formula with Ctrl+Shift+Enter confines the output to as many cells as selected. In Dan's case, he selected only one cell and the formula doesn't automatically spill, so the formula is confined only to that cell. Excel seems to treat that cell as if it only contains that array element. If you select 2 cells and enter an array formula then =INDEX(A1#,2) will work but =INDEX(A1#,3) returns #REF!.
Edit2: It is possible with the FORMULATEXT Function as #DickKusleika suggested. Here is a function adapted from ExcelJet that does the job.
=LET(
DesiredIndex, 1,
ArrayFormulaRef, A1,
Formula, FORMULATEXT(ArrayFormulaRef),
FormulaLen, LEN(Formula),
CSVStart, FIND("{",Formula,2),
CSV, MID(LEFT(Formula,FormulaLen-1),CSVStart+1,FormulaLen),
TRIM(MID(SUBSTITUTE(CSV,",",REPT(" ",LEN(CSV))),(DesiredIndex-1)*LEN(CSV)+1,LEN(CSV)))
)

Find maximum value in each row or column of an Excel array that is not a cell reference

I need to find the maximum of a column in an array inside of a LET call that is not a cell reference using one cell.
This works for arrays that are cell references but when I try to use it on an array that is not a cell reference, it fails.
For example, using this data.
12 2 3
3 7 5
7 8 9
=LET(Rng,$A$1:$C$3,SUBTOTAL(4,OFFSET(INDEX(Rng,1,1),,COLUMN(Rng)-MIN(COLUMN(Rng)),ROWS(Rng)))) returns [12, 8, 9] as you would expect but =LET(Rng,$A$1:$C$3*1,SUBTOTAL(4,OFFSET(INDEX(Rng,1,1),,COLUMN(Rng)-MIN(COLUMN(Rng)),ROWS(Rng)))) returns #VALUE
Is this possible to do in a single cell or am I forced to use multiple cells?
Edit:
Desired output is
12 8 9
I have a formula that works for arrays that are tied to cells, I don't have one that works for arrays that are not tied to cells, like the result of a calculation within a LET
Edit 2:
Ultimately I need something that works for a 36x36 array.
Here's an alternative approach using more conventional indexing and sorting:
=LET(sa,SEQUENCE(9,1,0),sb,SEQUENCE(1,3,3,3),col,INDEX(A1:C3,MOD(sa,3)+1,QUOTIENT(sa,3)+1),
sortcol,SORTBY(col,QUOTIENT(sa,3),1,col,1),INDEX(sortcol,sb))
The idea is to convert the 2d array into a 1d array (col), then sort it first by the column number in the original array, then by the values in the array. Finally extract every third element from the resulting array. This shows the steps separately:
This is the overall result:
I forgot that the point of the question was that it should work for an array as well as a range:
=LET(sa,SEQUENCE(9,1,0),sb,SEQUENCE(1,3,3,3),col,INDEX(A1:C3*1,MOD(sa,3)+1,QUOTIENT(sa,3)+1),
sortcol,SORTBY(col,QUOTIENT(sa,3),1,col,1),INDEX(sortcol,sb))
or
=LET(sa,SEQUENCE(9,1,0),sb,SEQUENCE(1,3,3,3),col,INDEX(RANDARRAY(3,3),MOD(sa,3)+1,QUOTIENT(sa,3)+1),
sortcol,SORTBY(col,QUOTIENT(sa,3),1,col,1),INDEX(sortcol,sb))
General form for max of columns of rectangular array (e.g. 4 rows by 3 columns)
=LET(arr,A1:C4*1,r,ROWS(arr),c,COLUMNS(arr),sa,SEQUENCE(r*c,1,0),sb,SEQUENCE(1,c,r,r),col,
INDEX(arr,MOD(sa,r)+1,QUOTIENT(sa,r)+1),sortcol,SORTBY(col,QUOTIENT(sa,r),1,col,1),INDEX(sortcol,sb))
For min, just change sort order:
=LET(arr,A1:C4*1,r,ROWS(arr),c,COLUMNS(arr),sa,SEQUENCE(r*c,1,0),sb,SEQUENCE(1,c,r,r),col,
INDEX(arr,MOD(sa,r)+1,QUOTIENT(sa,r)+1),sortcol,SORTBY(col,QUOTIENT(sa,r),1,col,-1),INDEX(sortcol,sb))
General form for max of rows of rectangular array
=LET(arr,A1:C4*1,r,ROWS(arr),c,COLUMNS(arr),sa,SEQUENCE(r*c,1,0),sb,SEQUENCE(r,1,c,c),col,
INDEX(arr,QUOTIENT(sa,c)+1,MOD(sa,c)+1),sortcol,SORTBY(col,QUOTIENT(sa,c),1,col,1),INDEX(sortcol,sb))
Again for min just change sort order:
=LET(arr,A1:C4*1,r,ROWS(arr),c,COLUMNS(arr),sa,SEQUENCE(r*c,1,0),sb,SEQUENCE(r,1,c,c),col,
INDEX(arr,QUOTIENT(sa,c)+1,MOD(sa,c)+1),sortcol,SORTBY(col,QUOTIENT(sa,c),1,col,-1),INDEX(sortcol,sb))
Alright, this is a long stretch and very experimental. I feel there could be a better way. For the following to work I used the function ARRAYTOTEXT() which I believe only is available for users with access to Microsoft365's insiders programm. Nonetheless, here is my attempt:
Formula in E1:
=LET(X,ARRAYTOTEXT(TRANSPOSE(A1:C3),1),TRANSPOSE(FILTERXML("<y><t><s>"&SUBSTITUTE(SUBSTITUTE(MID(X,2,LEN(X)-2),",","</s><s>"),";","</s></t><t><s>")&"</s></t></y>","//s[not(.< preceding-sibling::*)][not(.<= following-sibling::*)]")))
You can test the working on an array by replacing the A1:C3 reference to something like RANDARRAY(4,4,1,100,1)

In Excel, can I sum a column of cells based on whether or not a given value is in each of the rows?

Within Excel, is there an array formula or something else that could shorten the formula below? This is only an example going through 12 rows. The actual formula would have thousands of rows, which is why I'd like to find a way to write this formula much shorter. I've considered and tried SUMIF and SUMPRODUCT in addition to what's below, but I haven't found a way for it to check for a specified value in multiple columns, and then doing that for many rows, like a FOR loop would do. The below formula is in Cell J3. I have attached an image of the spreadsheet example.
=SUM(
IF(ISNUMBER(MATCH($I$3,$B3:$E3,0)),$F3,0),
IF(ISNUMBER(MATCH($I$3,$B4:$E4,0)),$F4,0),
IF(ISNUMBER(MATCH($I$3,$B5:$E5,0)),$F5,0),
IF(ISNUMBER(MATCH($I$3,$B6:$E6,0)),$F6,0),
IF(ISNUMBER(MATCH($I$3,$B7:$E7,0)),$F7,0),
IF(ISNUMBER(MATCH($I$3,$B8:$E8,0)),$F8,0),
IF(ISNUMBER(MATCH($I$3,$B9:$E9,0)),$F9,0),
IF(ISNUMBER(MATCH($I$3,$B10:$E10,0)),$F10,0),
IF(ISNUMBER(MATCH($I$3,$B11:$E11,0)),$F11,0),
IF(ISNUMBER(MATCH($I$3,$B12:$E12,0)),$F12,0),
IF(ISNUMBER(MATCH($I$3,$B13:$E13,0)),$F13,0),
IF(ISNUMBER(MATCH($I$3,$B14:$E14,0)),$F14,0))
Use SUMPRODUCT like this:
=SUMPRODUCT($F$3:$F$14*(MMULT(N($B$3:$E$14=I3),TRANSPOSE(COLUMN($B$3:$E$14)^0))>0))
This is an array formula and it needs to be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode.
Improved solution
=SUMPRODUCT($F$3:$F$14,CEILING(((($B$3:$B$14=$I3)+($C$3:$C$14=$I3)+($D$3:$D$14=$I3)+($E$3:$E$14=$I3))/4),1))
Idea : 4 is the number of rows/week. Use ceiling() to normalize value instead of int() n sqrt().
Expansion note : just add another row condition & adjust 4 to the number of rows.
Past solution (for reference)
=SUMPRODUCT($F$3:$F$14,INT(SQRT(INT(SQRT(($B$3:$B$14=$I3)+($C$3:$C$14=$I3)+($D$3:$D$14=$I3)))+($E$3:$E$14=$I3))))
should do.
idea : while $B$3:$B$14=$I3 part is creating an array of 0 n 1, the INT() n SQRT() function 'force' the '+' sum to become 1 even if there is more than 1 match in the same month.
please share if it works/not. (:
p/s : (note for expansion) Upon dissecting the int() and sqrt() function, you can see that sqrt(3)=1.73205 , sqrt(2)1.414213, sqrt(1)=1 and its int() results the same value (1) . So let say you want to add more rows, just use 'bundle' 3 rows together in one int(sqrt(__)) function, recursively.

MAX + Left Function Excel

I am trying to get the max value of a column based on the Left function
What I am doing is the following :
These are the results I get when i write this into column C :
=MAX(LEFT(A:A, 2))
But what I truly want is to get in column C the max value of all column A not for each cell.
So the result should be in this case 90 for all rows.
What should be the formula here?
Just another option that gets entered normally:
=AGGREGATE(14,6,--LEFT($A$1:INDEX(A:A,MATCH("ZZZ",A:A)),2),1)
Array formulas will calculate the enitre referenced array. So care should be taken to limit the number of iterations to only the data set.
The $A$1:INDEX(A:A,MATCH("ZZZ",A:A)) part of the formula does that. It finds the last cell in column A with data in it and sets that as the upper bound. So in this instance the reference range is A1:A3. But, it will grow dynamically as data in Column A is added, so no need to change the formula each time data is added.
Update 2
Here is another solution which I think is better than my original (below)
=INT(SUMPRODUCT(MAX(SUBSTITUTE(A:A,"-",".")*1)))
it can be entered as normal (just Enter)
Orignal Answer
You need numbers and arrays
=MAX(IFERROR(LEFT(A:A,2)*1,0))
Let's break this down. Multiplying by turns your strings into numbers - since Left only returns a string
LEFT(A:A,2)*1
Unfortunately this method returns #Value if you multiply an empty string by 1. You will definitely have some empty strings in the range A:A so we wrap the whole thing with an IFERROR function.
IFERROR(LEFT(A:A,2)*1,0)
Now we still need excel to treat this as an array (i.e. a whole column of numbers, rather than just one number). So we put the MAX formula in and enter it with Ctrl+Shift+Enter rather than just Enter. The result is that the formula looks like this in the formula bar
{=MAX(IFERROR(LEFT(A:A,2)*1,0))}
which would return 90 in your example, as required
Update 1
If you are using Excel 2013 or later, you can also use the NUMBERVALUE function
=MAX(NUMBERVALUE(LEFT(A:A,2)))
again, enter it with Ctrl+Shift+Enter

Using INDEX(MATCH()) to return the n'th value of a cell

I have a simple worksheet with 2 columns
I want to get all the results(on column "H") (I can get only the first occurrence,I want to know if I can get the others) that contains the value from cell G1, is that possible without a macro ? any way of doing it would be appreciated...Any ideas?
You can use ROW() and SMALL() to get those instead of MATCH() since this always gets the first match.
=IFERROR(INDEX($C$4:$C$7,SMALL(IF($D$4:$D$7=$G$1,ROW($D$4:$D$7)-(ROW()-1)),ROWS($D$4:D4))),"Null")
So, if the array $D$4:$D$7=$G$1 returns true (i.e. the value equals that in G1), you will get the row numbers of these values, in this case, you will get 4 and 6. All the other will return False.
After some processing with -(ROW()-1), the 4 and 6 become 1 and 3. those two values are what will be fed to the INDEX.
SMALL() then picks the smallest, starting with the 1st (you get 1 from ROWS($D$4:D4)) and when you drag the formula down, the ROWS become ROWS($D$4:D5) which gives 2, and SMALL ends up taking the 2nd smallest value, which is 3.
EDIT: Forgot to mention. You have to array enter the above formula. To do this, type the combination keys of Ctrl+Shift+Enter after typing the formula (edit the formula again if necessary) instead of Enter alone.

Resources