This question already exists:
VBA Function not storing variable
Closed 5 years ago.
I am reposting this question, because I haven't gotten an answer and I still can't figure out what I'm doing wrong. My latest efforts to resolve the problem on my own are detailed below the code.
Original post: VBA Function not storing variable
I have included the code of my function. I mostly scrapped this together from things I found online, because I am very much an amateur coder. I am trying to take the trendline of a graph and use it for a mathematical calculation. When I step through this code, it works great. However, when I call the function from another sub, it gives me an error. Error 9: Subscript out of range. When I debug, it shows me the line a = spl(0). The real problem is that the variable "s" remains empty. Why?
Function TrendLineLog() As Double
Dim ch As Chart
Dim t As Trendline
Dim s As String
Dim Value As Double
' Get the trend line object
Set ch = ActiveSheet.ChartObjects(1).Chart
Set t = ch.SeriesCollection(1).Trendlines(1)
' make sure equation is displayed
t.DisplayRSquared = False
t.DisplayEquation = True
' set number format to ensure accuracy
t.DataLabel.NumberFormat = "0.000000E+00"
' get the equation
s = t.DataLabel.Text '<--------- ACTUAL PROBLEM HERE
' massage the equation string into form that will evaluate
s = Replace(s, "y = ", "")
s = Replace(s, "ln", " *LOG")
s = Replace(s, " +", "")
s = Replace(s, " - ", " -")
spl = Split(s, " ")
a = spl(0) '<----------- DEBUG SAYS HERE
b = spl(1)
c = spl(2)
y = 0.5
..... Math stuff
End Function
I have tried adding the creation of the chart to the function to avoid an error with "Active Sheet". I also tried pasting this code into my sub instead of calling a separate function. Still nothing. When I debug and highlight the t.DataLabel.Text, it shows me the correct value, but for some reason s is not saving that value. In the Locals window, t has value, but s is blank (" ").
Yes, of course you will get an error on the line you pointed out. You are calling spl(0) as though it is its own function, though you did not define spl() as a sub (function) anywhere in this code. Or, alternatively (more likely) you are calling it as an array, which also throws up some flags.
Make sure you are defining spl in your code. You never do this. Add line:
Dim spl(1 to 3) As String
Then you should find that spl(1), spl(2), and spl(3) are what you desire.
Related
Sub initialize()
For boxNum = 1 To 10
vaultValuesForm.Controls("h" & boxNum).Value = ""
vaultValuesForm.Controls("d" & boxNum).Value = ""
Next boxNum
vaultValuesForm.Show
End Sub
Sub button_Populate_Click()
Dim array_h(9) As String, array_d(9) As String
For boxNum = 0 To 9
array_h(boxNum) = vaultValuesForm.Controls("h" & (boxNum + 1)).Value
array_d(boxNum) = vaultValuesForm.Controls("d" & (boxNum + 1)).Value
Next boxNum
Call populateTable(array_h(), array_d())
End Sub
Sub populateTable(array_0() As String, array_1() As String)
For x = 1 To 4
ThisDocument.Bookmarks("bd" & x).Range.Text = array_0(0)
Next x
End Sub
I have tested the functionality of this code at various points, and it works flawlessly right up until this line:
ThisDocument.Bookmarks("bd" & x).Range.Text = array_0(0)
Specifically, until it reaches = array_0(0). In its current state, reaching this point in the Sub results in "Run-time error '5941': The requested member of the collection does not exist." Same deal when I originally tried using = array_0(x) (which is ultimately what I'm trying to accomplish). However, if replaced with something direct such as = "AA", it works. How do I phrase this bit properly to set the bookmark values to those within the array?
Note: In case you're wondering, the arrays are being referenced and passed properly; I tested this by changing the loop to comments and using MsgBox() with various array elements.
The answer from comments. The issue I wasn't aware of was that the bookmarks were being deleted after running the module, so it wouldn't work again unless the bookmarks were created again.
Are you sure the bookmarks bd1...bd4 are still there in the document? Because a bookmark's range.text deletes the bookmark, so if you want to be able to repeat the bookmark text assignments you have to recreate the bookmarks after assigning the texts. FWIW I ran your code and it was fine when bd1..bd2 etc. existed but threw 5941 the next time. (This is quite a common problem!) – slightly snarky Sep 3 at 8:37
So, for the official answer to my question, the way I had done it initially is how; it just couldn't be repeated.
I am using the below code in Blue prism for filtering in excel for multi criteria.
But i am not able to filter multi criteria for Not equal to scenario.
Dim wb As Object
Dim excel as Object
Dim range as Object
Try
wb = GetWorkbook(Handle, Workbook)
excel = wb.Application
range = excel.Range(FRange)
Dim listOfValues as Array
listOfValues = Split(FCriteria,";")
wb.worksheets(Worksheet).select
range.select
range.Autofilter(FCol,listOfValues,7)
Success = True
Catch e As Exception
Success = False
Message = e.Message
Finally
wb = Nothing
End Try
Please help me tweaking the script
I'm almost sure that there is no filter option to set a "negative list". You can specify either a (positive) list of values (this is what your code does so far, for this you have to set the 7 as third parameter), or you can give a maximum of 2 individual criteria (in Excel, choose "Custom Filter" to set them.
You should play with the filter directly in Excel and try to set it like you want. Once you are satisfied with it, clear the filter, record a macro and repeat the filtering. Go to the VBA editor and see what's in there. It is straightforward to translate this into C# code.
But:
It's not possible to set any filtering by code (neither C# nor VBA) that you cannot set via the Excel GUI
I would question what you are trying to do. Since you are using Blue Prism, you should be trying to access the underlying data in a BP Collection(VB DataTable), rather than applying a filter, which is a visual tool for humans to further play with the interface. The robot will still have to do something with the filtered data, and it far easier to write code to proceed with data during the loop.
Otherwise use the Filter Collection Page of the 'Utilities - Collection Manipulation' VBO to get a filtered collection.
Also you are using VBA Split function, when you should use Split in VB as a method of the String.
Try this for a new page in the 'Utilities - Collection Manipulation' VBO(untested):
Dim NewRow As DataRow
Collection_Out = Collection_In.Clone
Dim Select_Concat As String
Select_Concat = "NOT(" & fieldName & " = '" & [String].Join("' OR " & fieldName & " = '", FCriteria.Split(";"c)) & "')"
For Each parentRow As DataRow In Collection_In.Select(Select_Concat)
NewRow = Collection_Out.NewRow
For Each c As DataColumn In NewRow.Table.Columns
NewRow(c.ColumnName) = parentRow(c.ColumnName)
Next c
Collection_Out.Rows.Add(NewRow)
Next parentRow
NewRow = Nothing
Collection_In = Nothing
Inputs: Collection_In(Collection), fieldName(Text), FCriteria(Text)
Outputs: Collection_Out(Collection)
You first need to get the entire range into an unfiltered Collection(which will be your Collection_In to this page, and then get the filtered Collection out....
For example, if i have a complex functions like this one: =IFERROR(CELL("address",INDEX(D:D,MATCH(A2;D3:D$750;0)+ROW(A2);1;1));"") i want to convert it into hyperlink which points to cell returned by that formula. And yeah, i know that it could be done by wrapping it with =HYPERLINK("[file_name.xlsm]" & %formula%) BUT, first of all, it looks very ugly and uncomfortable to read in the big sparse data, and second - it is very inconvenient to copy such data-blocks between Excel instances (and then replace filename).
So i wonder - is it possible to convert such output into cell-link for current file and sheet with readable text like "D156" in place of "[long_file_name.xlsm]$D$156"?
P.S. obviously i can make a custom function which returns current filename and put same formula into second argument of HYPERLINK func, but obviously it will be counter-effective, especially when i have huge chunks of data. So i want to wrap all this into 1 function...
P.P.S. Don't sure if this will be appropriate (it's kinda offtopic for this question), but it will be fine if the same functionality could be done with macro instead of custom function. So the macro should find all the duplicates inside of currently selected range and make hyperlinks (in cell with same offset on different sheet with offset +1, the next sheet after currently active) to next occurrence of every duplicate. The rest i can manage by myself.
UPD1:
Getting filename is not a problem, as i said in P.S., i can write a function like this:
Function GetCurFilename()
GetCurFilename= "[" & ActiveWorkbook.Name & "]"
End Function
but i still need somehow to send part of the first HYPERLINK's argument as second one!
UPD2:
I guess it should be done like this:
Function MakeLinkArgs(cAdrs) As SomeType_interpreted_as_2_arguments
If (cAdrs = "") Then
MakeLinkArgs(1) = ""
MakeLinkArgs(2) = ""
Else
MakeLinkArgs(1) = "[" & ActiveWorkbook.Name & "]" & cAdrs
MakeLinkArgs(2) = Replace(cAdrs, "$", "")
End If
End Function
So this func could be used like this: =HYPERLINK( MakeLinkArgs( IFERROR(CELL("address",INDEX(D:D,MATCH(A2;D3:D$750;0)+ROW(A2);1;1));"") ) )
But still no luck finding type which could be interpreted as 2 arguments (don't sure if such exists at all)...
Not totally sure what you are asking, but I think that =CELL("filename") will help you. This returns both the filename and the sheet. Just extract the sheet name and file name from what this returns. Also portable for cut/paste to other workbooks/worksheets.
Well, i didn't found a way to send multiple arguments with single result from other function, but i found a side way to do what i want. It looks like a dirty hack to me, but i don't sure, maybe it's the way to do things in Excel VBA...
So first it's needed to add a public variable at the top of the Module:
Public sBuff As String
Next, these 2 functions should be added after public var declaration:
Function MakeCLink(cAdrs)
If (cAdrs = "") Then
MakeCLink = ""
sBuff = ""
Else
MakeCLink = "[" & ActiveWorkbook.Name & "]" & cAdrs
sBuff = Replace(cAdrs, "$", "")
End If
End Function
Function sBuf() As String
sBuf = "" & sBuff
End Function
And after compilation it could be used in formulas as such: =HYPERLINK( MakeCLink(IFERROR(CELL("address",INDEX(D:D,MATCH(A2,D3:D$750,0)+ROW(A2),1,1)),"")), sBuf() )
Yes, it's ugly, but works as magic for me =)
I am trying to run as a macro a custom Excel function XpathOnUrl from the add-in called SeoTools by Niels Bosma. The function runs fine and it seems that I am able to store its result in a variable. This variable can then be correctly output to an Excel cell, but when I try to look for a string in it in the next part of the macro, I get the error Run-time error '13': Type mismatch. From what I understand from here, the function returns an array, but when I try to access it as the first item of the array, I get the same error. I tried to convert the variable into a string with CStr, but no luck there either. What am I missing?
Here's the problematic part of the code:
WebSite = Sheet1.Range("A1")
contactPage = Application.Run("XPathOnUrl", WebSite, "//a[contains(translate(#href, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),""contact"")]", "href")
MsgBox TypeName(contactPage) 'trying to find out the data type results in 'Error'
If Left(contactPage(0), 4) = "http" Then
Sheet1.Range("B1").Value = contactPage
ElseIf InStr(contactPage, "/") = 1 Then
Sheet1.Range("B1").Value = WebSite & contactPage
End If
Just to make it clear: the problem starts only with conditional statements. If I assign the value of the variable directly to a cell like this Sheet1.Range("B1").Value = contactPage, it outputs the correct result.
Here's a easy workaround:
Make XpathURL spit its return to a range. Then, use Range.value to assign the return to a contactpage and clear the range using .Clearcontents property. I think Application.Run is not letting XpathURL return get to contactpage.
Edit: Added the comment below:
Sheet1.Range("B1").Value = Application.Run("XPathOnUrl", WebSite, "//a[contains(translate(#href, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),""contact"")]", "href")
contactPage = Sheet1.Range("B1").Value
Did you try the Formula and FormulaR1C1 functions? Just my assumptions.
Try and let us know if it helped.
Sheet1.Range("B1").Formula = WebSite & contactPage
or
Sheet1.Range("B1").FormulaR1C1 = WebSite & contactPage
What does the MsgBox TypeName(contactPage) display?
The following code is part of a function that is being called for each file the program finds during it's search through a directory.
set searchname = objFSO.OpenTextFile(server.mappath(filename),1, true)
do until searchname.AtEndOfStream
lineData = lcase(searchname.ReadLine())
if instr(lineData,s)>0 then
instances = instances + 1
end if
Loop
This the part of my code I'm confused with. This code worked perfectly yesterday. I made a few edits but mostly with HTML and CSS. When I pulled this code out today to optimize it better, I just keep getting a permission denied error with this line.
set searchname = objFSO.OpenTextFile(server.mappath(filename),1, true)
The problem is that the folder the code scans through contains some files that have more than one word. So it's essential that I get the "filename" variable within double quotes. I've been looking everywhere for a solution and none of them have been helpful yet. Is this even possible or is there a workaround?
Any help would be much appreciated here.
EDIT: As requested here's the code that gets the filename. Although no idea how it pertains to my question.
Function filesearch(name)
ext = instr(name,".txt")
vdirectoryvalue = instr(name,"Files\")+6
idname = mid(name,vdirectoryvalue,ext)
vdirectory = len(idname) - 4
filename = left(idname,vdirectory)
instances = 0
set searchname = objFSO.OpenTextFile(server.mappath(idname), 1, true)
do until searchname.AtEndOfStream
lineData = lcase(searchname.ReadLine())
if instr(lineData,s)>0 then
instances = instances + 1
end if
Loop
End Function