I am new to Macros below is what I wrote to be able to do a lookup through a function. When I debug all the values seem to work well except that at the end it gives error about circular reference.
I am making a call to below like: = GetCost("abc")
Public Function GetCost(arg1 As String)
'
' GetCost Macro
'
Dim fName As String
Dim searchItem As String
Dim result As Integer
fName = Range("B4").Value
searchItem = Chr(34) & arg1 & Chr(34)
expression = "=VLOOKUP(" & searchItem & "," & fName & ", 2, FALSE)"
ActiveCell.Value = ActiveSheet.Evaluate(expression)
result = Evaluate(expression)
GetBOMCOst = result
End Function
The circular reference is because you are trying to change the value of the cell containing the function:
remove ActiveCell.Value = ...
change result= Evaluate to result =application.caller.parent.Evaluate(expression)
Also fName=Range("B4").value should use a fully qualified reference (Sheet.Range)
And this function should be made volatile otherwise it will not recalculate whenever the value in Range("B4") changes
You need to remove your active cell reference as suggested by other.
Also, its looks like, your lookup range is incorrect. Change the lookup expression as below, in case, if you have your value in A and B column.
expression = "=VLOOKUP(" & searchItem & "," & "A:B" & ", 2, FALSE)"
And, it looks like you trying to return the value to your calling function. In VBA, to return a variable, you need to assign the variable to a function name. i.e. change your last line GetBOMCOst = result to GetCost = result
Try the below updated version of the code.
Public Function GetCost(arg1 As String) ' ' GetCost Macro ' Dim fName As String Dim searchItem As String Dim result As Integer
searchItem = Chr(34) & arg1 & Chr(34)
expression = "=VLOOKUP(" & searchItem & "," & "A:B" & ", 2, FALSE)"
result = Evaluate(expression)
GetCost = result
End Function
Related
I'm experiencing the following issue:
Sheets("Workbook 1 ").Range("N" & integerv).Formula = "=Workbook2!""" & column_string & """ 3)"
(integerv being some integer) keeps returning " Object-defined or application-defined error".
column_string is a string variable obtained by
column_string = Split(Cells(a,b).Address(True,False), "$")(0)
essentially just containing the column letter of the cell I want to reference.
Am I passing the string variable incorrectly ? And what would my code have to look like if I wanted to additionally pass an integer variable instead of "3" ?
Any help appreciated, I've been stuck with this for a fair while now.
Just a Thought or Two About Formulas in VBA
How would you write your formula for column A in Excel?
=Workbook2!A3
In VBA you would do this:
rng.formula = "=Workbook2!A3"
You can aslo write it like this:
rng.Formula = "=Work" & "book2" & "!A3"
or like this:
rng.Formula = "=" & "Work" & "book" & "2" & "!" & "A3"
But of course in this case (your post) you wanna write it like this:
rng.Formula = "=Workbook2!" & "A" & "3"
Now when you make it dynamic, you know what to do:
rng.Formula = "=Workbook2!" & column_string & "3"
As FunThomas mentioned in the comments you can use a variable (and you should especially when there is a more complicated one). Then you could create a new Sub just for the formula and write...
Dim strFormula As String
strFormula = "=Workbook2!" & "A" & "3"
...and in the Immediate window (CTRL+G) see the written result with...
Debug.Print strFormula
...and modify the formula until you get it right.
You my try the formula in this way:
Dim b As Long, column_string As String
b = 2: column_string = "C" 'the result simulation of your extraction using Split
Sheets("Workbook 1 ").Range("N" & integerv).Formula = "=Workbook2!" & column_string & 3
'having the b variable value, no need to extract the column letter:
Sheets("Workbook 1 ").Range("N" & integerv).Formula = "=Workbook2!" & cells(3, b).Address(0, 0)
I need to iterate the "condition" in the formula "sumif". The variable type is "string" but my code doesn´t work. I can´t keep the condition of the string of the variable. The code is an example if i can solve the problem with the "variable". I just need to simply iterate that variable. The code is:
Private Sub cmd_psps_Click()
Dim prueba As Integer
Dim var As String
prueba = Sheets("PRUEBAS").Index
var = "a"
Sheets(prueba).Cells(5, 16).Formula = "=sumif(" & Range(Cells(4, 13),
Cells(8, 13)).Address() & "," & var & "," & Range(Cells(4, 14), Cells(8,
14)).Address() & ")"
End Sub
The problem is in ," & var & ", with that code the cell in worksheet looks like:
=SUMAR.SI($M$4:$M$8;a;$N$4:$N$8)
But I need:
=SUMAR.SI($M$4:$M$8;"a";$N$4:$N$8)
I can use the condition with a simpletext for example "a". But I need to iterate the condition with a variable.
Not strictly an answer, but for the code readability, when using manual Excel's formula construction, it's good to have handy function to concatenate strings:
Function FormatString(s As String, ParamArray args() As Variant)
Dim x%
For x = LBound(args) To UBound(args)
s = Replace$(s, "{" & x & "}", args(x))
Next
FormatString = s
End Function
The usage:
Sub G()
MsgBox FormatString("I have {0} apples and {1} oranges", 10, 20)
End Sub
You use placeholders {0}, {1} etc. for parameters. This is like what C#'s String.Format does.
Thus, your formula will look like this:
Sub G()
Dim sFormula$
Dim var$
var = "A1"
sFormula = FormatString( _
"=SUM({0},{1},{2})", _
Range(Cells(4, 13), Cells(8, 13)).Address(), _
var, _
Range(Cells(4, 14), Cells(8, 14)).Address())
End Sub
Now the code looks more readable.
So I've learned pivot tables are tricky, so are string variable syntax, and so is the use of quotations in VBA. I'm trying (and failing) to use all three!
I am trying to collect one piece of data from a very large/complex pivot table for a large number of job numbers. I want to select a job number in A1, and have the pivot table formula automatically update with the selected job number to return a number result.
I am currently having two problems: 1) the syntax to get the job number in quotations for the 'getpivotdata' formula isn't working and 2)it is dropping off the zeros in the job number when it executes the code.
My code is listed below, when I execute it I am hoping to see this formula populate in A2:
=GETPIVOTDATA("Part Number",' Parts Status '!$A$8,"CHAR_FIELD3","11-008","MATERIAL STATUS MASTER","Avail")
but instead get:
=GETPIVOTDATA("Part Number",' Parts Status '!$A$8,"CHAR_FIELD3",11-8,"MATERIAL STATUS MASTER","Avail")
My code is here
Sub Macro1()
Dim jobnumber As String
jobnumber = Worksheets("Macros test").Cells(1, "A").Value
Sheets("Macros test").Select
Range("A2").Select
ActiveCell.FormulaR1C1 = _
"=GETPIVOTDATA(""Part Number"",' Parts Status '!R8C1,""CHAR_FIELD3""," & jobnumber & ",""MATERIAL STATUS MASTER"",""Avail"")"
If you need the quotes there, change it to this:
"=GETPIVOTDATA(""Part Number"",' Parts Status '!R8C1,""CHAR_FIELD3"",""" & jobnumber & """,""MATERIAL STATUS MASTER"",""Avail"")"
Also, you can add a temporary msgbox after setting jobnumber to make sure it is what you think it is:
MsgBox(jobnumber)
I've always used the Chr() function to deal with quotes in VB. Double quote is Chr(34) and single quote is Chr(39). The problem you experienced was that while jobnumber is defined as a string you didn't have the double quotes around it. The result is Excel removed the leading zeros on the formula 11-008, which Excel saw as Eleven minus eight. Adding the double quotes (Chr(34)) around jobnumber solved the problem.
I always break these long strings into smaller pieces so that I can see what I've typed. The code below uses a little private function to build your pivot string.
Public Sub PivotTest()
Dim jobnumber As String
jobnumber = Worksheets("Macros test").Cells(1, "A").Value
Sheets("Macros test").Select
Range("A2").Select
ActiveCell.FormulaR1C1 = BuildPivotString(jobnumber)
End Sub
Private Function BuildPivotString(ByRef jobNum As Variant) As String
Dim retVal As String
retVal = "=GETPIVOTDATA("
retVal = retVal & Chr(34) & "Part Number" & Chr(34)
retVal = retVal & ",' Parts Status '!R8C1,"
retVal = retVal & Chr(34) & "CHAR_FIELD3" & Chr(34) & ","
retVal = retVal & Chr(34) & jobNum & Chr(34) & ","
retVal = retVal & Chr(34) & "MATERIAL STATUS MASTER" & Chr(34) & ","
retVal = retVal & Chr(34) & "Avail" & Chr(34) & ")"
BuildPivotString = retVal
End Function
1. I was trying to answer VBA UDF to split string array and got an unpleasant results during computing my UDF.
Public Function mytest(src, dest)
dest.Parent.Evaluate "test(" & src.Address(False, False) & ", " & dest.Address(False, False) & ")"
mytest = "wut"
End Function
Sub test(src As Range, dest As Range)
Dim chr, rows, cols
rows = 0
cols = 0
For chr = 1 To Len(src.Value)
Select Case Mid(src.Value, chr, 1)
Case ","
rows = rows + 1
Case ";"
cols = cols + 1
rows = 0
Case Else
Cells(dest.Row + rows, dest.Column + cols).Value = Cells(dest.Row + rows, dest.Column + cols).Value & Mid(src.Value, chr, 1) '
End Select
Next chr
End Sub
Expected results:
Formula results:
Can someone explain why does it double Value of the cell?
When I debugged test using
Sub ffs()
Call test(Cells(1, 1), Cells(3, 1))
End Sub
I got expected results, so I guess the problem is not in the test Sub?..
2. Whenever I try to add more parameters to Function and Sub (for example delimiters) Function doesn't Evaluate Sub at all
Public Function CellToRange(src, dest, DelimL, DelimC)
dest.Parent.Evaluate "test(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"
CellToRange = "wut"
End Function
Sub CTR(src As Range, dest As Range, Delim1, Delim2)
Dim chr, rows, cols
rows = 0
cols = 0
For chr = 1 To Len(src.Value)
Select Case Mid(src.Value, chr, 1)
Case Delim1
rows = rows + 1
Case Delim2
cols = cols + 1
rows = 0
Case Else
Cells(dest.Row + rows, dest.Column + cols).Value = Cells(dest.Row + rows, dest.Column + cols).Value & Mid(src.Value, chr, 1) '
End Select
Next chr
End Sub
Please help ._. and thanks in advance.
Solution:
Thanks Billy and Charles Williams.
Change
dest.Parent.Evaluate "CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"
To
dest.Parent.Evaluate "0+CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"
Thanks everyone!
The problem lies with the Worksheet.Evaluate method which is being used to get round the restriction that a UDF is not allowed to modify the worksheet structure.
Consider this code
Option Explicit
Public Function dummyudf() As String
Debug.Print "Calling Evaluate method"
ActiveSheet.Evaluate "testsub()"
Debug.Print "Returning From Evaluate method"
dummyudf = "done"
End Function
Sub testsub()
Debug.Print "testsub running"
End Sub
Sub testmacro()
Dim s As String
Debug.Print "testmacro running"
s = dummyudf
End Sub
The UDF dummyudf() uses the Evaluate method to invoke the Sub called testsub(). These are analagous to mytest and test in part 1. of the OP and to CellToRange and CTR in part 2 but are stripped down to the bare minimum.
testsub() can also be invoked directly as a macro. A second macro testmacro invokes dummyudf as a function in VBA.
The following output was obtained from the Immediate Window:
As can be seen
when invoked as a macro: testsub() behaves as expected
when dummyudf() is invoked as a UDF on the worksheet (for example by adding the formula =dummyudf() to cell A1 the Evaluate method appears to call testsub() twice
when dummyudf() is invoked as a function in VBA by running testmacro() as a macro the Evaluate method appears to call testsub() twice.
The documentation here suggests that the Name argument of the Worksheet.Evaluate method should be the name of an object, so it is a bit surprising that it is possible supply the name of a Sub. That it also seems to call any such Sub twice, is even more surprising but does underline the advice given in YowE3K's answer about not using this hack in a UDF. I'd go further: don't use Worksheet.Evaluate with any Sub.
1) It evaluates once when the formula is triggered, and again when cell A3 is updated by the function (as it is one of the cells the formula is dependent on).
2a) You are calling the wrong subroutine (test instead of CTR)
2b) You need to call your second function using something like
=CellToRange(A1;A3;""",""";""";""")
or else change the line in your code calling CTR to be
dest.Parent.Evaluate "CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", """ & DelimL & """, """ & DelimC & """)"
3) I strongly recommend that you do not use this sort of hack to get a UDF to update cells other than the one containing the function.
I'm writing a macro which should run queries to transfer data from excel to a Access database, and everything is working fine, however, if I want to use a String in one of these queries, I've to type them like this:
'" & Lijn & "'
I know that the following code (which is written in VBA) is way easier to write in Javascript, by using question marks and setString:
VBA:
Dim ShiftsQ As String
ShiftsQ = "INSERT INTO Shifts(Lijn, Operator, Ploeg, Teamleider) VALUES ('" & Lijn & "', '" & Operator & "', '" & Ploeg & "', '" & Teamleider & "');"
Javascript:
var ShiftsQ = SQL.prepareStatement(INSERT INTO Shifts(Lijn, Operator, Ploeg, Teamleider) VALUES (?, ?, ?, ?);
ShiftsQ.setString(1, Lijn);
ShiftsQ.setString(2, Operator);
ShiftsQ.setString(3, Ploeg);
ShiftsQ.setString(4, Teamleider);
Is there anyway to write the VBA code like the Javascript one?
As far as I know, there is nothing like the .NET string.Format() method VBA. But you could write your own version of such a function that uses deputys and returns a formatted string.
Private Sub Main()
' Your actual query
' The deputys are indexed in curley brackets (inspired from the .NET syntax of the equivalent function, making your code easy to read for .NET programmers)
Dim qry As String
qry = "SELECT {0}, {1} FROM {2} WHERE {3}"
' The values to set for the deputys in your query
Dim parameters(3) As String
parameters(0) = "firstname"
parameters(1) = "lastname"
parameters(2) = "users"
parameters(3) = "userID = 'M463'"
' For demo purposes, this will display the query in a message box
' Instead of the MsgBox, you would use the formatted query to execute against the database
MsgBox FormatString(qry, parameters)
End Sub
' This is where the magic happens, the deputys in the given string will be replaced with the actual values from the provided array
Private Function FormatString(strInput As String, paramValues() As String)
' This will be our return value
Dim strOutput As String
strOutput = strInput
' Verify that the given count of parameters matches the given count of deputys in the input string
Dim maxParamIndex As Integer
maxParamIndex = UBound(paramValues)
Dim deputyCount As Integer
For i = 1 To Len(strOutput) + 1 Step 1
If Mid(strOutput, i, 3) = "{" & deputyCount & "}" Then
deputyCount = deputyCount + 1
End If
Next
' If there is a mismatch between the count of parameters and the count of deputys, display exception message and exit the function
' Adding +1 to maxParamIndex is neccessary, as maxParamIndex refers to the maximum index (starting at 0, not 1) of the given array and deputyCount refers to the actual count of deputys (starting at 1)
If maxParamIndex + 1 <> deputyCount Then
MsgBox "Number of deputys has to match number of parameters for the given string:" & vbCrLf & strInput, vbCritical, "Exception in Function FormatString"
FormatString = ""
End If
' Iterate through the array and replace the deputys with the given values
For i = 0 To maxParamIndex Step 1
strOutput = Replace(strOutput, "{" & i & "}", paramValues(i))
Next
' return the formatted string
FormatString = strOutput
End Function
Result of example:
If I face this problem I would simply solve it on my own (althoug there might be other "standard" solutions), by defining my own simple, global function (put in in any standard code module)
Public Function S_(str as String) as String
S_ = chr(39) & str & chr(39)
End Function
ShiftsQ = "INSERT INTO Shifts(Lijn, Operator, Ploeg, Teamleider) VALUES (" & S_(Lijn) & ", " & S_(Operator) & ", " & S_(Ploeg) & ", " & S_(Teamleider) & ");"
This way, I will follow a simple and systematic rule in all my project, that is call S_(param) on any parameter of text type in my queries...