I have the following excel formula
IFERROR(MID(B7;FIND(" ";B7)+1;1);"")
I want to convert this now to VBA, but I have trouble integrating the Find function here. This is what I have so far
IfError(Mid(Sheets("MySheet").Cells(x, 7),Sheets("MySheet").Cells(x, 7).Find " " + 1, 1), "")
But that's clearly not correct. Can somebody help me with this?
Something like so, called like get_first_char_after_space("testing xys")
Function get_first_char_after_space(strInput As String) As String
If InStr(1, strInput, Chr(32)) > 0 Then
get_first_char_after_space = Left(Split(strInput, Chr(32))(1), 1)
Else
get_first_char_after_space = vbNullString
End If
End Function
You can find the solution for IFERROR at the following SO question: https://stackoverflow.com/a/29390944/6908282.
And VBA has a Range.Find menthod which you can use to replaicate FIND formula.
Documentation link: https://learn.microsoft.com/en-us/office/vba/api/excel.range.find
Here is a UDF to do the job of your worksheet function. As you see, an error can be avoided in VBA. I presume that the entire function could be compressed into one line but whatever benefit that might entail would come at the cost of readability and possibly stability. As it is, the code is very robust. It easily transplants into a bigger project as, no doubt, is your intention.
Function NextChar(Cell As Range) As String
' IFERROR(MID(B7;FIND(" ";B7)+1;1);"")
Dim Txt As String
Dim n As Integer
Txt = Cell.Value
n = InStr(Txt, " ")
If n Then NextChar = Mid(Txt, n + 1, 1)
End Function
If called from the worksheet the function would be =NextChar(B7). Called from code it would be Debug.Print NextChar(Sheet1.Cells(7, 2))
Related
I have found a macro to evaluate strings. It is working fine with most formulas. But it wont evalute my sumifs-formulas.
The VBA for WM_Eval() goes like this:
Function wm_Eval(myFormula As String, ParamArray variablesAndValues() As
Variant) As Variant
Dim i As Long
'
' replace strings by values
'
For i = LBound(variablesAndValues) To UBound(variablesAndValues) Step 2
myFormula = RegExpReplaceWord(myFormula, variablesAndValues(i),
variablesAndValues(i + 1))
Next
'
' internationalisation
'
myFormula = Replace(myFormula, Application.ThousandsSeparator, "")
myFormula = Replace(myFormula, Application.DecimalSeparator, ".")
myFormula = Replace(myFormula,
Application.International(xlListSeparator), ",")
'
' return value
'
wm_Eval = Application.Evaluate(myFormula)
End Function
If I type wm_Eval("1+1") it works like a charm. But if I do:
="sumifs(b2:b10,a2:a10,"&D2&">=2"&D2&")" where d2=" it returns #Value.
So the formula it should evaluate would be: sumifs(b2:b10,a2:a10,">=2")
I am accustomed to danish excel - so it might just be something very very simple I am missing.
I feel you should give some more information about why you need this function, as I'm sure there'd be a better of way of achieving the task than the code above.
There are a number of reasons the function is returning an error. The main one is that your formula syntax isn't right. The wm_Eval() function requires a formula argument and then pairs of variable and value parameters. You haven't included the code for your RegExpReplaceWord() function, but I'd imagine it runs some kind of replacement of values for pseudo formula variables. Without seeing that part of your code, it's difficult to guess at the syntax, but a mock-up of the principle would be something like:
=wm_Eval("SUMIFS(A1:A4,B1:B4,{a})","{a}",">=1")
where {a} is replaced by ">=1"
Data type is also an issue, as you can see in the following syntax:
=wm_Eval("SUMIFS(A1:A4,B1:B4,{a})","{a}",1)
I don't want to be too brutal, but this code is pretty poorly constructed and there's no way that it'll be able to parse all the different formulae with various passed in parameters. Yes it works for SUM but that's because you don't pass in any variable-value pairs. I would steer away from this solution and come up with something more robust. If for example you want to gather string versions of your formula, why not just pass in the range containing the formula and deal with it from there?
I can tell you that I have written a routine to parse values from formulas. It is long and complex and took many, many hours to develop including some WINAPI calls and hooks, so you have a lot of work ahead of you if you wish to continue with your current approach.
However, I guess I ought to show you how your current function works, so I've mocked-up a RegExpReplaceWord() function and commented out that internationalisation code:
Public Function wm_Eval(myFormula As String, ParamArray variablesAndValues() As Variant) As Variant
Dim i As Long
'
' replace strings by values
'
For i = LBound(variablesAndValues) To UBound(variablesAndValues) Step 2
myFormula = RegExpReplaceWord(myFormula, variablesAndValues(i), variablesAndValues(i + 1))
Next
'
' internationalisation
'
' myFormula = Replace(myFormula, Application.ThousandsSeparator, "")
' myFormula = Replace(myFormula, Application.DecimalSeparator, ".")
' myFormula = Replace(myFormula, Application.International(xlListSeparator), ",")
'
' return value
'
wm_Eval = Application.Evaluate(myFormula)
End Function
Private Function RegExpReplaceWord(myFormula As String, variable As Variant, value As Variant) As String
If VarType(value) = vbString Then
value = """" & value & """"
End If
RegExpReplaceWord = Replace(myFormula, variable, value)
End Function
So in your worksheet cell, you can add the functions as shown in the quote blocks above.
In summary, I wouldn't continue with your current approach or you will be plagued with so many errors that your lost 3 days will seem like nothing.
I'm trying to get all the content between multiple parenthesis and comma delimiting them. So for example
A1 contains
thisfile.jpg (/path/to/file.jpg), thisfile2.jpg (/path/to/file2.jpg)
and B1 should look like
/path/to/file.jpg, /path/to/file2.jpg
If it's just one entry I can get what I need with this:
MID(A1,FIND("(",A1)+1,FIND(")",A1)-FIND("(",A1)-1)
But that only returns the first one, I need to be for each parenthesis. The amount of parenthesis in each row will vary.
I am sure there are better solutions out there with formulas only. Yet, I cannot help you there. But the following UDF is surely also a feasible solution. Just copy this code into an empty module:
Option Explicit
Public Function GetPaths(strTMP As String)
Dim i As Long
Dim varArray As Variant
varArray = Split(strTMP, "(")
For i = LBound(varArray) To UBound(varArray)
If InStr(1, varArray(i), ")") > 0 Then
GetPaths = GetPaths & ", " & Mid(varArray(i), 1, InStr(1, varArray(i), ")") - 1)
End If
Next i
GetPaths = Mid(GetPaths, 3)
End Function
Afterwards, you can use this formula in column B as follows: =GetPaths(A1).
I am working on a budget in excel 2007. It was written by someone else and contains many sheets and many formulas. I am trying to make the spreadsheet more efficient by creating an Input worksheet where all/most constants would be entered. To help with this process, I would like to be able to highlight all formulas that contain constants as well as highlight all constants (not in formulas) within my selection. Or, if easier, the opposite, highlight all formulas that do not contain constants within my selection. I am mainly dealing with numbers, not text.
Here are examples of formulas(=) with constants and just constants:
=82000-50000
=$A$2-50000
=A2-50000
=F133***.05**
50000
Here are examples of formulas(=) that do not contain constants:
=SUM(E8:P8)
=$C$51*'Servicing Detail'!$E$181
=K152
The closest answer I could find to my question was here: How to determine if a cell formula contains Constants?. But I believe this post to be specific to finding quotations within a formula as Siddharth Rout clarified in his last comment.
Any help would be greatly appreciated. Thank you. (This is my first post and hope that I have formatted correctly. Apologies in advance)
You can parse formulas using the SPLIT function in VBA. E.g. the code below works for the examples you have given. Returns TRUE if formula contains constants, returns N/A if it is not a formula and FALSE otherwise.
Probably you have to adapt a little bit so it works in all cases, but it's a good starting point.
Function HasConstant(r As Range) As Variant
Application.Volatile
Dim formula As String
Dim delimiters() As String
Dim delimiter As Variant
Dim Components() As String
Dim component As Variant
Dim chars As Integer
delimiters() = Split("+ - * / = & ( ) ,")
If r.HasFormula Then
formula = Right(r.formula, Len(r.formula) - 1)
Do Until formula = ""
chars = Len(formula)
component = formula
For Each delimiter In delimiters
Components = Split(formula, delimiter)
If Len(Components(0)) < chars And Len(Components(0)) > 0 Then
component = Components(0)
chars = Len(component)
End If
Next
If IsNumeric(Replace(component, ".", Application.International(xlDecimalSeparator))) Then 'IsNumeric(component)
HasConstant = True
Exit Function
ElseIf Left(CStr(component), 1) = Chr(34) And Right(CStr(component), 1) = Chr(34) Then
HasConstant = True
Exit Function
End If
If chars < Len(formula) Then
formula = Right(formula, Len(formula) - chars - 1)
Else
formula = ""
End If
Loop
Else
HasConstant = CVErr(xlErrNA)
Exit Function
End If
HasConstant = False
End Function
Example:
I wanted to know a way to reference part of a value in a cell in Excel in VBA. For instance if in cell A32 I have stored 4200 cc. I would like to reference only the 4200 for calculations in a macro in VBA. So like one can reference strings or lists in python with indices, I'd like to be able to do the same. Thank you!
Something like this(?):
Dim tmpArr
tmpArr = Split(Range("A32"), " ")
'any calculation in vba in this way:
Range("B32") = tmpArr(0) / 100
'results with 42 in cell B32
EDIT If there is any doubt about recognition of number for first part of the split results you could make additional conversion in this way:
'conversion to Double type
Range("B32") = CDbl(tmpArr(0))/100
You may create a function to split the content of the cell and then handle the parts you want separately.
Dim content As Variant
content = Split(Range("A32").Value, " ")
Get the cell value as a string s and return the numeric part?
Function GetNumber(s As String)
Dim j As Long
While Not IsNumeric(Left(s, 1))
If Len(s) <= 1 Then
Exit Function
Else
s = Mid(s, 2)
End If
Wend
GetNumber = Val(s)
End Function
I'm trying to write a function that merges multiple rows of text in a column into a single cell based on a pre determined count. My goal is to generate a flexible function to aid in compiling / interperting large quantaties of data. The code I've written returns #NAME? and I cant figure out where the error is. My code is as follows:
Function vmrg(countref As Integer, datref As Integer) As String
If IsEmpty(ActiveCell.Offset(0, -countref)) Then % check if cell containing count is blank
vertmerge = "N/A" % if blank, state N/A
Else
Dim datlst(0 To ActiveCell.Offset(0, -countref).Value - 1) As String
Dim i As Integer
For i = 0 To ActiveCell.Offset(0, -countref).Value - 1
datlst(i) = ActiveCell.Offset(i, -datref).Text %fill array with data
End
vertmerge = datlst(0)
For i = 1 To ActiveCell.Offset(0, -countref).Value - 1 % merge array to a single string
vertmerge = vertmerge & ", " & datlst(i)
End
End
End Function
I have matlab and some C++ experience but this is the first time I've used VBA so my syntax is probably odd in some areas and wrong in others. Ideally I would like to reference the cells where the data and count info are stored, but for now I'm hoping to correct my syntax and set a jumping off point for further development of this function. Any reccomendations are appreciated.
Code Rev_1: I still have an output of #NAME? but I think I've corrected(?) some of the issues
Function vertmerge(countref As Range, datref As Integer) As String
If IsEmpty(countref) = True Then
vertmerge = "NA"
Else
Dim datlst(0 To countref.Value - 1) As String
Dim i As Integer
For i = 0 To countref.Value - 1
datlst(i) = countref.Offset(i, datref).Text
Next i
vertmerge = datlst(0)
For i = 1 To countref.Value - 1
vertmerge = vertmerge & ", " & datlst(i)
Next i
End
End Function
You are doing some dangerous things here!
First - you are referencing "ActiveCell" from inside a function; but you have NO IDEA what cell will be active when the function runs! Instead, pass the target cell as a parameter:
=vmrg("B6", 5, 6)
and change your function prototype to
Function vmrg(r as Range, countref as Integer, datref as Integer)
Now you can reference things relative to r with
r.Offset(1,2)
etc.
Next - you are never assigning anything to vmrg. In VBA, the way a function returns a value is with (in this case)
vmrg = 23
You are assigning things to a variable called vertmerge - but that is not the name of your function. At least add
vmrg = vertmerge
Just before returning. That might do it. Without a full sample of your spreadsheet I can't help you more.