Can I write UDF inside an Evaluate formula strings in VBA? - excel

I have defined the next formula to enter it as an Array Formula:
=IF(LenTableRange(Hoja1!$B$15)=Hoja1!$B$4,IF(OFFSET(LenTableRange(Hoja1!$A$15),0,5)="",LenTableRange(Hoja1!$A$15)))
For some extra info:
- LenTableRange returns a range object, the body of the function is:
'Passing a cell as a range, it returns
Function LenTableRange(R As Range) As Range
Application.Volatile
Set LenTableRange = Range(R, R.End(xlDown))
End Function
The data i'm taking is Strings in $A$15 > column, numbers in $B$15 > column. Both columns can have repeated values, that's why I filter it, and the column that referes to OFFSET(LenTableRange($B$15);0;5) referes to dates, that could have no value or empty cell.
As you see it has a user defined function in it.
The UDF 'LenTableRange' works fine.
When I enter the whole formula or the name into a cell it works fine, but when I try the following in VBA:
'I think I have to escape the double quotes as so.
Dim StrForm As String
StrForm = "=IF(LenTablaRango(Hoja1!$B$15)=Hoja1!$B$4,IF(OFFSET(LenTablaRango(Hoja1!$A$15),0,5)="""""",LenTablaRango(Hoja1!$A$15)))"
Debug.Print Application.Evaluate(StrForm)
It gives error 2015.
I suspect it is not reading UDFs inside the formula properly.
Is it really possible to do this?
What is the best way to solve this problem?

Related

VBA Loop through named ranges - using Indirect on variants

I'm trying to loop through the multiple named ranges, every range is just one cell, on a column, so it starts with item1, item2...item100.Each of these ranges has a cell reference in it (like "AM100").
I want to be able to set up a for/while loop that just selects(and does something) each of the referenced cells in those named ranges.
I'm stuck at VBA not considering "item"&"1" the same with "item"&"i" when i =1 and from what I deduct this is purely a data type issue. It's able to refer to a pure string range but when it's variant/string type (which any concatenation of string or converted string variable results in).
item1 range has in it $AM$10 reference. That cell, has a value in it. Eventually I want to change the values in multiple similar cells by referring to the name ranges that hold their reference.
Without the "for" loop, I've tested the following:
Sub test()
Dim i as integer
i=1
'These don't work
Range([indirect("item"&CSTR(i))]).Select
Range([indirect("item"&i)]).Select
'What works is, but this is not useful since I want to loop through i:
Range([indirect("item" & "1")]).Select
Range([indirect("item1")]).Select
Sub Test()
Dim oDefName As Name
For Each oDefName In ThisWorkbook.Names
If Left(UCase(oDefName.Name), 4) = "ITEM" Then
Range(oDefName.RefersToRange.Value).Select
End If
Next
End Sub
Note: There is no error checking to ensure that the value within the named range is actually a cell reference.
Edit- Above is how I would solve the problem. Indirect is an in cell function and not usable directly in vba. Below is how you would get the indirect functionality in VBA with a counter.
Range(Range("Item" & oCounter).Value).Select

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")

Formula relative to cell calling the formula in excel

I'm using Excel XP.
My problem is that I wrote a very short function ti simplify one task.
The code is the following:
Function getLastValue() as Integer
getLastValue = ActiveCell.End(xltoLeft).Value
End Function
Then I want to fill by dragging that cell downwards, but the same value will be copied, no matter what value appears in each row. So I wanted to make it relative to the cell calling the function so that it goes to the nearest cell in the left that has a value and copy return it in the cell calling the function.
I made a Sub for it but I want to have it in the form of a function to apply it on arbitrary cells.
Application.Caller is what you want.
You will also need Application.Volatile in order to update the formula when your worksheet changes.
Function getLastValue() As Integer
Application.Volatile
getLastValue = Application.Caller.End(xlToLeft).Value
End Function

Excel VBA Function to Return Address of Value or Text in Array Only Returns #VALUE

The following code prints the expected cell address in the immediate window but I can't get the cell in the excel spreadsheet for which I type the function formula to return anything but #VALUE!. For example, in one of my tests, my immediate window is printing the proper result as $F$8 - the proper cell reference of my intended match.
I've also tried varying format options inside the spreadsheet cell where I'm using the function/formula but with the same result. I'm obviously missing something incredibly simple but I'm embarrassed to say I'm tired of researching what I am hoping will be a very simple answer by this forum.
Public Function CellRefInRange(ByVal ItemSearched As Variant, ByVal aRange As Range) As String
Dim cell As Range
Dim CellRefInRange2 As String
For Each cell in aRange
If ItemSearched = cell.Value then CellRefInRange2 = cell.Address
Next Cell
Debug.Print CellRefInRange2
CellRefInRange = CellRefInRange2
End Function

Excel, indirect reference to range of sheets

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!

Resources