Excel, indirect reference to range of sheets - excel

Trying to use INDIRECT to reference a range of sheets, and a range of cells in those sheets, looking for the MAX. Neither of these work:
=MAX(INDIRECT("1:"&last_sheet&"!G"&ROW()&":K"&ROW()))
=MAX(INDIRECT("1:6!G"&ROW()&":K"&ROW()))
However, this does (but only looks at one sheet: 1):
=MAX(INDIRECT("1!G"&ROW()&":K"&ROW()))
And so does this (but doesn't use INDIRECT):
=MAX('1:6'!G6:K6)
It seems to me that INDIRECT simply cannot be used with a range of sheets. Please tell me I'm wrong and why.

It looks like you're probably correct. The following workaround is ugly, but it works.
=MAX(MAX(INDIRECT("'1'!B1:C2")),MAX(INDIRECT("'2'!B1:C2")),MAX(INDIRECT("'3'!B1:C2")))

You can paste the below function into the VBA editor, and it will produce the results you're looking for. It returns the max of whatever range you specify, across all of the sheets in the workbook. Use it just like a regular function, ie =MultiMax(A1). It also accepts an INDIRECT as a parameter.
Function MultiMax(r As Range) As Long
Dim s As Worksheet
Dim a() As Long
Dim m As Long
ReDim a(0 To 0)
For Each s In ThisWorkbook.Sheets
m = Application.WorksheetFunction.Max(s.Range(r.Address).Value)
ReDim Preserve a(0 To UBound(a) + 1)
a(UBound(a)) = m
Next
Dim y As Integer
Dim m1 As Long
For y = 0 To UBound(a)
If a(y) > m1 Then
m1 = a(y)
End If
Next
MultiMax = m1
End Function

Similar to the above solution, you could also try an array formula. However, this will require you to do a MAX function on each sheet (preferably in the same cell on each sheet). For example, on sheet '1', you have MAX(B2:C2) in cell D1, and then the same on sheet '2', sheet '3', etc. Then on your summary sheet, use this array formula:
=MAX(N(INDIRECT(ADDRESS(1,4,,,ROW(INDIRECT("A1:A"&last_sheet))))))
Then be sure to hit Ctrl+Shift+Enter to enter it as an array formula.
This assumes "last_sheet" is some integer value like 6 for example, then makes a range string of it ("A1:A6"), passes this to INDIRECT which passes it to ROW() giving you an array from 1:6. This array is used as the list of sheet names for ADDRESS which creates an array of references at cell D1 on each of the six sheets. The array is passed to INDIRECT which returns #VALUE! errors until you pass the array of errors to N(). Finally, max returns the largest value in the array. You can use "Evaluate Formula" to see how it breaks down step by step, but hopefully this is a good starting point for you!

Related

Convert vlookup to more than 255 characters via Excel VBA

I am looking for reverse vlookup with more than 255 characters in Excel VBA.
This is the formula based one which I took from this website.
=INDEX(F2:F10,MATCH(TRUE,INDEX(D2:D10=A2,0),0))
I have try to convert it in VBA. Here below sample code
Sub test()
'concat
Range("i1") = WorksheetFunction.TextJoin(" ", True, Range("g1:h1"))
'lookup
Sal1 = Application.WorksheetFunction.Index(Sheets("sheet1").Range("a1:a2"), Application.WorksheetFunction.Match(True, Application.WorksheetFunction.Index(Sheets("sheet1").Range("i1:i1") = Range("i1").Value, 0), 0))
'=INDEX($W$3:$W$162,MATCH(TRUE,INDEX($W$3:$W$162=U3,0),0))
End Sub
It works well but it didn't when i change the range("i1:i1") to range("i1:i2")
I'm not sure what that worksheet formula does that =INDEX(F2:F11,MATCH(A2,D2:D11,FALSE)) doesn't do.
This part Index(Sheets("sheet1").Range("i1:i2") = Range("i1").Value, 0) is comparing a 2-d array to a single value, which should result in a Type Mismatch error. Whenever you reference a multi-cell range's Value property (Value is the default property in this context), you get a 2-d array even if the range is a single column or row.
You could fix that problem with Application.WorksheetFunction.Transpose(Range("D1:D10")) to turn it into a 1-d array, but I still don't think you can compare a 1-d array to a single value and have it return something that's suitable for passing into INDEX.
You could use VBA to create the array's of Trues and Falses, but if you're going to go to that trouble, you should just use VBA to do the whole thing and ditch the WorksheetFunction approach.
I couldn't get it to work when comparing a single cell to a single cell like you said it did.
Here's one way to reproduce the formula
Public Sub test()
Dim rFound As Range
'find A2 in D
Set rFound = Sheet1.Range("D1:D10").Find(Sheet1.Range("A2").Value, , xlValues, xlWhole)
If Not rFound Is Nothing Then
MsgBox rFound.Offset(0, 2).Value 'read column f - same position as d
End If
End Sub
If that simpler formula works and you want to use WorksheetFunction, it would look like this
Public Sub test2()
Dim wf As WorksheetFunction
Set wf = Application.WorksheetFunction
MsgBox wf.Index(Sheet1.Range("F2:F11"), wf.Match(Sheet1.Range("A2").Value, Sheet1.Range("D2:D11"), False))
End Sub
Function betterSearch(searchCell, A As Range, B As Range)
For Each cell In A
If cell.Value = searchCell Then
betterSearch = B.Cells(cell.Row, 1)
Exit For
End If
betterSearch = "Not found"
Next
End Function
i found this code from above link and it is useful for my current search.Below examples i try to get value..
Kindly consider Row 1 to 5 as empty for A and B column because my table always start from Row 6
Row
A Column
B Column
6
54
a
7
55
b
8
56
c
VBA Code:
Sub look_up ()
Ref = "b"
look_up = betterSearch(Ref, Range("B6:B8"), Range("A6:A8"))
End Sub
it show Empty while use Range("B6:B8"), Range("A6:A8")
but when changing the range from B6 and A6 to B1 and A1 (Range("B1:B8"), Range("A1:A8") )it gives the value...
My question is "can get the values from desired range"
Expressing matches via VBA
I like to know if there (are) any possibilities to convert this formula.
=INDEX(F2:F10,MATCH(TRUE,INDEX(D2:D10=A2,0),0))
So "reverse VLookUp" in title simply meant to express the (single) formula result via VBA (btw I sticked to the cell references in OP, as you mention different range addresses in comments).
This can be done by simple evaluation to give you a starting idea:
'0) define formula string
Dim BaseFormula As String
BaseFormula = "=INDEX($F$2:$F$10,MATCH(TRUE,INDEX($D$2:$D$10=$A2,0),0))"
'1) display single result in VB Editor's immediate
Dim result
result = Evaluate(BaseFormula)
Debug.Print IIf(IsError(result), "Not found!", result)
On the other hand it seems that you have the intention to extend the search string range
from A2 to more inputs (e.g. till cell A4). The base formula wouldn't return a results array with this formula,
but you could procede as follows by copying the start formula over e.g. 3 rows (note the relative address ...=$A2 to allow a row incremention in the next rows):
'0) define formula string
Dim BaseFormula As String
BaseFormula = "=INDEX($F$2:$F$10,MATCH(TRUE,INDEX($D$2:$D$10=$A1,0),0))"
'2) write result(s) to any (starting) target cell
'a)Enter formulae extending search cells over e.g. 3 rows (i.e. from $A2 to $A4)
Sheet3.Range("H2").Resize(3).Formula2 = BaseFormula
'b) optional overwriting all formulae, if you prefer values instead
'Sheet3.Range("H2").Resize(3).Value = Tabelle3.Range("G14").Resize(3).Value
Of course you can modify the formula string by any dynamic replacements (e.g. via property .Address(True,True,External:=True) applied to some predefined ranges to obtain absolute fully qualified references in this example).
Some explanations to the used formulae
The formula in the cited link
=INDEX(F2:F10,MATCH(TRUE,INDEX(D2:D10=A2,0),0))
describes a way to avoid an inevitable #NA error when matching strings with more than 255 characters directly.
Basically it is "looking up A2 in D2:D10 and returning a result from F2:F10" similar to the (failing) direct approach in such cases:
=INDEX(F2:F11,MATCH(A2,D2:D11,FALSE))
The trick is to offer a set of True|False elements (INDEX(D2:D10=A2,0))
which can be matched eventually without problems for an occurence of True.
Full power by Excel/MS 365
If, however you dispose of Excel/MS 365 you might even use the following much simpler function instead
and profit from the dynamic display of results in a so called spill range.
That means that matches can be based not only on one search string, but on several ones (e.g. A1:A2),
what seems to solve your additional issue (c.f. last sentence in OP) to extend the the search range as well.
=XLOOKUP(A1:A2,D2:D10,F2:F10,"Not found")

Find the sum of a cell from difference worksheets without referencing sheet names

I have a workbook with multiple sheets. All sheet names follow this scheme "Sensor Status", "Sensor Status(1)" and so on. Is there a way to get the SUM of I4 from all these sheets without having to reference each individual sheet name? I tried this however I am getting the ref error. "A5" has the value of "sensor" as I am attempting to use wild cards in the sheet reference of the formula.
=SUM(INDIRECT("'*"&A5&"*'!K4"))
You can do something like this for the Sensor Status(#)
=SUM(INDIRECT("'Sensor Status("&ROW(1:5)&")'!K4"))
This is an array formula an must be confirmed with Ctrl-Shift-Enter instead of Enter.
NOTE: May be able to use SUMPRODUCT instead of SUM to avoid the CSE entry.
But it requires an array, which the ROW(1:5) provides. If this is not ideal then vba or listing the sheets in a column and referring to that would be needed.
See this answer with more options: How can you sum the same cell across multiple worksheets by worksheet index number in excel?
You can do a hybrid of vba and formula.
In a module attached to the workbook put:
Function SHEETNAME() As Variant
Application.Volatile
Dim temp() As Variant
ReDim temp(1 To Application.Caller.Parent.Parent.Worksheets.Count) As Variant
Dim i As Long
For i = 1 To Application.Caller.Parent.Parent.Worksheets.Count
temp(i) = Worksheets(i).Name
Next
SHEETNAME = temp
End Function
This returns all the worksheet names as an array. Then use something like this:
=SUMPRODUCT((ISNUMBER(SEARCH(A5,SHEETNAME())))*SUMIF(INDIRECT("'"&SHEETNAME()&"'!K4"),">0"))
in VBA:
Function SumbyCell(rng As Range) As Double
Dim sum As Double
Dim ws As Worksheet
For Each ws In ThisWorkbook.Sheets
sum = sum + ws.Range(rng.Address).Value2
Next ws
SumbyCell = sum
End Function
The function will work without depending on the sheet name.
Call the function by type: = Sumbycell(I4)
with function:
Suppose you have 2 sheets of names respectively: sheet1, sheet2
Your function is:
= CONCAT (SUM (INDIRECT ("sheet" & ROW (1: 2) & "! I4"))))
type: ctrl + shift + enter

Excel IRR Formula with non-contiguous ranges on a different sheet

I just realized that you can reference non-contiguous ranges in an IRR excel formula by enclosing a collection of references in parentheses (i.e. =IRR( (C18:C62,$B$1) ). This is a useful feature but, from various attempts, I have concluded that it does not work when a range reference includes another sheet/workbook or when a range is constructed with an offset function. Does anyone know of a workaround for this or another way to approach the problem?
Thank you for all your help!
IRR Function:
Sytax : IRR(values, [guess])
Assuming data is in Range A1:A5 and Range B1 for calculating IRR.
Being obvious =IRR(A1:A5) will give correct result. But =IRR(A1:A5,B1) makes excel assume that B1 is [guess] argument of the formula as syntax states and hence gives result same as for =IRR(A1:A5). Now if you add another range in formula like =IRR(A1:A5,B1,C1), excel will give error stating too many arguments entered. Thus, for non-contiguous ranges, all the ranges needs to be enclosed in parenthesis as =IRR((A1:A5,B1)) (as you mentioned in the question). This makes Excel to interpret it as a single argument consisting of 2 distinct ranges.
Using Offset in IRR.
it does not work when a range is constructed with an offset function
Well, IRR does support OFFSET function.
=IRR(OFFSET(A1,0,0,5)) is same as =IRR(A1:A5)
=IRR((OFFSET(A1,0,0,5),OFFSET(A1,0,1))) is same as =IRR((A1:A5,B1))
Above two formulas using Offset will give you correct results.
Using ranges from multiple sheets in IRR
it does not work when a range reference includes another sheet/workbook
This is TRUE when you are using different worksheets in one formula. However, if you are referencing range from only one worksheet and is different from current worksheet (one in which you are entering formula) then IRR function will work. That means =IRR(Sheet2!A1:A5) will give desired result even if formula is entered in Sheet1. But, IRR does not allows to use ranges from multiple worksheets in one formula. Hence, =IRR((A1:A5,Sheet2!B1)) is incorrect if entered in Sheet1.
So to use multiple ranges from different sheets you can use UDF(User Defined Function) in VBA. Following might help.
Function IRR_DEMO(rng1 As Range, ParamArray rng2() As Variant) As Double
Dim elem As Variant
Dim i As Long, cntr As Long
Dim arr() As Double
ReDim arr(1 To rng1.count) As Double 'set size of arr as per rng1
cntr = 1
For Each elem In rng1 'first range
arr(cntr) = elem.Value 'put range values in array
cntr = cntr + 1
Next elem
For i = LBound(rng2) To UBound(rng2) 'all the ranges apart from first range and in rng2
ReDim Preserve arr(1 To UBound(arr) + rng2(i).count) As Double 'reset size of arr as per rng2(i)
For Each elem In rng2(i)
arr(cntr) = elem.Value 'put range values in array
cntr = cntr + 1
Next elem
Next i
IRR_DEMO = IRR(arr) 'use array in IRR
End Function
You can use above UDF as
=IRR_DEMO(A1:A5)
=IRR_DEMO(A1:A5,B1)
=IRR_DEMO(OFFSET(A1,0,0,5),OFFSET(A1,0,1))
=IRR_DEMO(Sheet2!A1:A5)
=IRR_DEMO(A1:A5,Sheet2!B1)
=IRR_DEMO(Sheet2!A1:A5,Sheet2!B1)
See images for reference.
Sheet1
Sheet2
The best way I found to add multiple ranges in IRR is to add brackets around the multiple ranges. With the brackets it seems to recognize the multiple ranges. Example is shown below:
=IRR( **(** B27:F27,Z27:AB27 **)**, 10%)

Minimum of Excel Array Formula with INDEX()

I'm having a little trouble with this array formula in Excel.
I have two ranges (columns of data) that correspond to the row and column number to an overall array that contains multipliers to be applied to a non-related function. I want to find the minimum multiplier that is found from the column/row references.
Let's assume the column number range is A1:A10 and the row number range is A15:A24 and the multiplier array is K4:M23. An inefficient way to do this is to do:
=MIN(INDEX(K4:M23,A15,A1),INDEX(K4:M23,A16,A2),etc...)
...but this will get cumbersome, especially if checking for errors, etc. Not to mention the memory usage if this function is called several thousand times (it just so happens to be).
So I thought about an array function:
{=MIN(INDEX(K4:M23,A15:A24,A1:A10))}
...but this only returns the first element in the array. If this function is entered as a multi-cell array formula, it handles it correctly, but it seems that as is, MIN is applied to each singular element and the function returns the original array size, not the single value of the minimum.
Any way around this?
If I understand your question correctly, the following VBA function should produce what you want.
The function takes three arguments: a reference to the array range; a reference to the row number range; and a reference to the column number range. It returns the minimum of the values in the cells corresponding to the row numbers and column numbers.
Function ArrayMin(MatrixRange As Range, RowRange As Range, ColRange As Range) As Double
Application.Volatile
Dim colNum As Long
Dim rowNum As Long
Dim cellVal As Double
Dim MinVal As Double
Dim i As Long
MinVal = 1000000 'a number >= than max array range value
For i = 0 To ColRange.Rows.Count - 1
rowNum = RowRange(1, 1).Offset(i, 0).Value
colNum = ColRange(1, 1).Offset(i, 0).Value
cellVal = MatrixRange(rowNum, colNum).Value
If cellVal < MinVal Then
MinVal = cellVal
End If
Next
ArrayMin = MinVal
End Function
It can be installed in the standard way by inserting a new standard VBA module in your workbook and pasting the code in.
I have never been able to operate on the output of index as if it were an array. What could be done instead is filter the original table of multipliers. To avoid helper cells this can be done within defined names
rowFilter: = SIGN( MATCH(rowIndex, selectedRows, 0))
columnFilter: = SIGN( MATCH(columnIndex, selectedColumns, 0 ) )
filteredMultipliers:= multipliers * rowFilter * columnFilter
The worksheet formula
= AGGREGATE( 15, 6, filteredMultipliers, 1 )
will identify the minimum value ignoring the intentional #N/A errors.
Thanks to a little inspiration by pbart, here's how I ending up doing it correctly... (a year and a half after I asked the question.)
I basically create a reference array the same dimensions as K4:M23 that has ones at the intersection of the column and row arrays and multiply them together.
=AGGREGATE(15,6,(IF(MMULT(TRANSPOSE(IFERROR(IF(A15:A24=COLUMN(1:20),1,0),0)),IFERROR(IF(A1:A10={1,2,3},1,0),0))=0,#N/A,1)*K4:M23),1)
Not sure it's the most efficient, but not too bad doing an array lookup without using Match or Index.

Excel Lookup return multiple values horizontally while removing duplicates

I would like to do a vertical lookup for a list of lookup values and then have multiple values returned into columns for each lookup value. I actually managed to do this after a long Google search, this is the code:
=INDEX(Data!$H$3:$H$70000, SMALL(IF($B3=Data!$J$3:$J$70000, ROW(Data!$J$3:$J$70000)-MIN(ROW(Data!$J$3:$J$70000))+1, ""), COLUMN(A$2)))
Now, my problem is, as you can see in the formula, my lookup range contains 70,000 rows, which means a lot of return values. But most of these return values are double. This means I have to drag above formula over many columns until all lookup values (roughly 200) return #NUM!.
Is there any possible way, I guess VBA is necessary, to return the values after duplicates have been removed? I'm new at VBA and I am not sure how to go about this. Also it takes forever to calculate having so many cells.
[Edited]
You can do what you want with a revised formula, not sure how efficient it will be with 70,000 rows, though.
Use this formula for the first match
=IFERROR(INDEX(Data!$H3:$H70000,MATCH($B3,Data!$J3:$J70000,0)),"")
Now assuming that formula in in F5 use this formula in G5 confirmed with CTRL+SHIFT+ENTER and copied across
=IFERROR(INDEX(Data!$H3:$H70000,MATCH(1,($B3=Data!$J3:$J70000)*ISNA(MATCH(Data!$H3:$H70000,$F5:F5,0)),0)),"")
changed the bolded part depending on location of formula 1
This will give you a list without repeats.....and when you run out of values you get blanks rather than an error
Not sure if you're still after a VBA answer but this should do the job - takes about 25 seconds to run on my machine - it could probably be accelerated by the guys on this forum:
Sub ReturnValues()
Dim rnSearch As Range, rnLookup As Range, rnTemp As Range Dim varArray
As Variant Dim lnIndex As Long Dim strTemp As String
Set rnSearch = Sheet1.Range("A1:A200") 'Set this to your 200 row value range
Set rnLookup = Sheet2.Range("A1:B70000") 'Set this to your lookup range (assume 2
columns)
varArray = rnLookup
For Each rnTemp In rnSearch
For lnIndex = LBound(varArray, 1) To UBound(varArray, 1)
strTemp = rnTemp.Value
If varArray(lnIndex, 1) = strTemp Then
If WorksheetFunction.CountIf(rnTemp.EntireRow, varArray(lnIndex, 2)) = 0 Then 'Check if value exists already
Sheet1.Cells(rnTemp.Row, rnTemp.EntireRow.Columns.Count).End(xlToLeft).Offset(0, 1).Value =
varArray(lnIndex, 2)
End If
End If
Next Next
End Sub

Resources