I am an occasional Excel user and stuck how to create a dynamic range.
After looking up a text in a table, how can I calculate the range next to this cell, up to the next empty row? Not using VBA.
Thanks for your help.
In H4, formula copied down :
=IFERROR(INDEX(INDEX(C:C,MATCH(F4,A:A,0)):C$1000,MATCH(G4,INDEX(B:B,MATCH(F4,A:A,0)):B$1000,0)),"")
Should you want a dynamic range,
Change C$1000 to INDEX(C:C,MATCH(9.9E+307,B:B)
and
Change B$1000 to INDEX(B:B,MATCH(9.9E+307,B:B))
Then
The H4 copied down formula become :
=IFERROR(INDEX(INDEX(C:C,MATCH(F4,A:A,0)):INDEX(C:C,MATCH(9.9E+307,B:B)),MATCH(G4,INDEX(B:B,MATCH(F4,A:A,0)):INDEX(B:B,MATCH(9.9E+307,B:B)),0)),"")
Edit :
As per Ron Rosenfeld's comment, "should B11 change to 24 and G4 change to 24"
The "Source Table" set up in Excel Table type for dynamic range growing purpose
and
The H4 formula change to :
=IFERROR(LOOKUP(9^9,Table1[price]/(LOOKUP(ROW(Table1[texture]),ROW(Table1[texture])/(Table1[texture]<>""),Table1[texture])=F4)/(Table1[length]=G4)),"")
Combining the Index() and Match() functions usually works well when using two conditions. However, you will need to fill out the entire column A with the "texture" list in order for the below formula to work.
=INDEX(<P1>, MATCH(TRUE, (<T1>=<T2>) + (<L1>=<L2>) > 1,0))
Where <P1> is your entire price column (ex. C2:C15)
Where <T1> is your entire texture column (ex. A2:A15)
Where <T2> is your texture lookup value cell
Where <L1> is your entire length column (ex. B2:B15)
Where <L2> is your length lookup value cell
Let's say that you input your texture value into cell F3, and your length value into cell F4. With the remaining columns remaining as they are in your image, you would use the following formula:
=INDEX(C2:C15, MATCH(TRUE, (A2:A15=F3) + (B2:B15=F4) > 1,0))
Now last time I had to use Index/Match I thought I had to place the formula into an array. However, the above seems to work without it.
If you notice that it's not working as expected, you can place into an array formula by clicking the cell that contains the formula, then clicking the formula box at the top. While in the formula box, simultaneously press Ctrl + Shift + Return. This should then place curly brackets around your entire formula if done properly, as such:
If you have O365 with the SEQUENCE function, you can use, for price:
=IF(G4="","",VLOOKUP(G4,INDEX($B:$C,SEQUENCE(MATCH(TRUE,ISBLANK(INDEX($B:$B,MATCH(F4,$A:$A,0)):INDEX(B:B,ROWS(B:B)-MATCH(F4,$A:$A,0))),0)-1,,MATCH(F4,$A:$A,0)),{1,2}),2,FALSE))
explanation:
get starting row:
MATCH(F4,$A:$A,0)
ending row will be the first blank row after the starting row:
MATCH(TRUE,ISBLANK(INDEX($B:$B,MATCH(F4,$A:$A,0)):INDEX(B:B,ROWS(B:B)-MATCH(F4,$A:$A,0))),0)
Construct the relevant array:
INDEX($B:$C,SEQUENCE(MATCH(TRUE,ISBLANK(INDEX($B:$B,MATCH(F4,$A:$A,0)):INDEX(B:B,ROWS(B:B)-MATCH(F4,$A:$A,0))),0)-1,,MATCH(F4,$A:$A,0)),{1,2})
The above might reduce (with wavy) to:
index(b:c,{9,10,11},{1,2}
Then it's just a matter of applying the VLOOKUP
A more understandable, but longer with more operations, formula available in O365 makes use of LET. The advantage is that one can use names which indicate what each section of the formula does.
For example:
=IF(G4="","",LET(startRow,MATCH(F4,$A:$A,0),numRows,MATCH(TRUE,ISBLANK(INDEX($B:$B,startRow):INDEX($B:$B,ROWS($B:$B)-startRow)),0)-1,
arr,INDEX($B:$C,SEQUENCE(numRows,,startRow),{1,2}),price,XLOOKUP(G4,INDEX(arr,0,1),INDEX(arr,0,2)),price))
Or, using VLOOKUP
=IF(G4="","",VLOOKUP(G4,LET(startRow,MATCH(F4,$A:$A,0),numRows,MATCH(TRUE,ISBLANK(INDEX($B:$B,startRow):INDEX($B:$B,ROWS($B:$B)-startRow)),0)-1,arr,INDEX($B:$C,SEQUENCE(numRows,,startRow),{1,2}),arr),2,FALSE))
Finally, for earlier versions of Excel, you can use this whopper where we replace the SEQUENCE function with a construct like: ROW(INDEX(A:A,firstRow):INDEX(A:A,lastRow))
=IF(G4="","",VLOOKUP(G4,INDEX($B:$C,ROW(INDEX($A:$A,MATCH(F4,$A:$A,0)):INDEX($A:$A,MATCH(F4,$A:$A,0)+MATCH(TRUE,INDEX($B:$B,MATCH(F4,$A:$A,0)):INDEX($B:$B,ROWS($B:$B))="",0)-2)),{1,2}),2,FALSE))
=ADDRESS(3,1) 'evaluates to $A$3
=ROW($A$3) 'evaluates to 3
Why can't I nest them?
=ROW(ADDRESS(3,1)) 'Gives an error.
Try:
=ROW(INDIRECT(ADDRESS(3,1)))
instead of using ADDRESS which returns a string, consider using INDEX which will return a cell reference. The general format of INDEX is:
INDEX(Range you want to look in, rows down from top row, columns right for first column)
so in order to reference your whole sheet like address would you would need to select the range of the entire sheet:
=INDEX($A$1:$XFD$1048576,3,1)
The above formula actually returns the cell reference of $A$3 ($ is due to 3 and 1 being hard coded) then turns around and displays the contents of $A$3. As a result you don't actually see the $A$3. On the interesting side of things it also means you can define a range with INDEX(...):INDEX(...). To finish off your formula you would nest the INDEX in your ROW function as follows:
=ROW(INDEX($A$1:$XFD$1048576,3,1))
This avoids the use of the volatile function of INDIRECT and some of its other restrictions.
It is possible to dynamically change the input cell address in a formula based on the content in another cell?
Let's say I have a spreadsheet (excel or libreoffice) with these cell values:
A1: 10
A5: 9
C1: 5
Instead of hardcoding =A1-A5 , I would like to do something like this: =A1-A(C1), that would be evaluated at run time to use cell A5 for the second input.
The non-volatile¹ solution would be the INDEX function.
'for Excel
=A1-INDEX(A:A, C1)
'for OpenOffice
=A1-INDEX(A$1:A$1048576; C1)
¹ Volatile functions recalculate whenever anything in the entire workbook changes, not just when something that affects their outcome changes. Examples of volatile functions are INDIRECT, OFFSET, TODAY, NOW, RAND and RANDBETWEEN. Some sub-functions of the CELL and INFO worksheet functions will make them volatile as well.
Use INDIRECT to take the value in C1 as a pointer to the row that you want in column A:-
=A1-INDIRECT("A"&C1)
(tested in Open Office and Excel 2010)
For this kind of dynamic reference, you need the INDIRECT function. It takes two arguments INDIRECT(reference,style)
reference, a text string containing a cell, a range of cells text or a named range
and style a boolean that if omitted or TRUE, indicates that reference is A1 style, and when FALSE, the reference is using the R1C1 style.
so in your case you can use
INDIRECT("A"&C1)
or
INDIRECT("R1C"&C1,FALSE)
I would like to create a table with products and formulas on a sheet (Sheet2). For different products, different formulas apply.
I would like to retrieve the formula from that table but use the row numbers from the row in Sheet1
How do I enter a formula which is evaluated with the correct row numbers from Sheet1.
I have a UDF eval that can evaluate a text string:
=eval(vlookup(Product;Table;2;false)
The formula retrieved from the Table should use the row number of the actual row that the eval() is on.
I tried the following:
="D"&ROW(Sheet2!$A16)&"/G"&ROW(Sheet2!$A16)&"/F"&ROW(Sheet2!$A16)&"*5"
This retrieves the formula but the eval() does not calculate the result.
In your example, product is located on Sheet2 in the 16th row of the first column of table. If you want the 16th row on the worksheet, you can use the MATCH function on a full column reference, discarding the structured references of the ListObject table or you can use MATCH on the structured table reference and compensate for the 'position within' table by adjusting by the table header row.
That's probably confusing so here is an example.
To find the actual row-on-the-worksheet where bcd resides you would use one of these formulas.
=MATCH("bcd", Sheet2!B:B, 0)
=MATCH("bcd", Table2[a], 0)+ROW(Table2[#Headers])
The first simply returns row 6 on the worksheet. The second returns 2 since bcd is in the second row of the ListObject's .DataBodyRange property and this is adjusted by the row that the .HeaderRowRange property is in; e.g. 2 + 4 = 6.
Now that everything is clear, all you need to do is use the result from one of those formulas as the row_number parameter in an INDEX function.
=INDEX(D:D, MATCH("bcd", Sheet2!B:B, 0))/
INDEX(G:G, MATCH("bcd", Sheet2!B:B, 0))/
INDEX(F:F, MATCH("bcd", Sheet2!B:B, 0))*5
'or,
=INDEX(D:D, MATCH("bcd", Table2[a], 0)+ROW(Table2[#Headers]))/
INDEX(G:G, MATCH("bcd", Table2[a], 0)+ROW(Table2[#Headers]))/
INDEX(F:F, MATCH("bcd", Table2[a], 0)+ROW(Table2[#Headers]))*5
Your own formula could have worked with a series of INDIRECT functions that convert constructed strings to actual cell references. However, INDIRECT is considered volatile and best avoided if possible.
=INDIRECT("D"&ROW(Sheet2!$A6))/INDIRECT("G"&ROW(Sheet2!$A6))/INDIRECT("F"&ROW(Sheet2!$A6))*5
This solution is based on the capability of Excel to reference the cells in R1C1 style. In order to use it, you will have to go to File -> Options -> "Formulas" tab, and check the box "R1C1 reference style" in "Working with Formulas" group.
In addition, your eval() function will have to be able to evaluate such kind of formulas.
Once, all of these is done, you only have to retrieve the formula.
In order to make the things clear, I will focus on an example.
The table below is the contents of Sheet1 (the "Database" of products).
The column "Formula" contains the relevant formula:
for apple: RC[-2]+RC[-1]
for banan: RC[-2]*RC[-1]
for lemon: RC[-2]/RC[-1]
The table below is the contents of Sheet2:
The column "Formula" here contains the formula, retrieved from Sheet1. The formula of the column "Formula" is as follows:
=FORMULATEXT(INDEX(Sheet1!R2C1:R4C4,MATCH(RC[-3],Sheet1!R2C1:R4C1,0),4))
The description of each of the functions, used here (formulatext, index and match) can be found in Excel help.
As it can be seen, the retrieved formula, represented in R1C1 style is correct in context of Sheet2, were the products are arranged differently, and may appear more than once. The only remaining work to do is to apply the eval() function, after it was adapted to evaluate R1C1-style referenced formulas.
I hope it helps.
I have this formula in excel, to return a row number:
=MATCH(INDIRECT(ADDRESS(ROW(),4)),DayOffRequests!$A$1:$A$100,0)
and it works just fine.
I would like to make a new name (DAYS_OFF_ROW) and assign it to this formula.
Here's what I did in the name manager:
But when I write this into a cell: =DAYS_OFF_ROW it says #VALUE! whereas when I write the same formula into the cell, it gives me the row number I am looking for.
Why is does it say #VALUE! and not the row number like the formula does?
First off Row() returns the row on which the cell resides. If you put Row() in indirect you will get an error since there is no cell for which Excel can find a Row(). So right off the bat, your formula is nonsense for a named range.
Second, even if there was some magic way for Excel to know which Row() you cared about here, Match doesn't return a range. Just a position in an array like "5" or "50". When you use a formula to define a named range, the result of the formula MUST be a range. So you could do another Indirect like =Indirect("A" & Match(foo,bar)) or something so that the result out of your formula actually refers to a range in your sheet.
It works if i set the name to this formula: =MATCH(!$D1,DayOffRequests!$A$1:$A$100,0)
Of course i have to be in field A1 when setting the name. Now its reference the fourth column and whichever row i am in.