Is an array stored as a string ?
Example :
Arr[0]="A"
Arr[1]="B"
Arr[2]="C"
Is this array stored like this "A,B,C" if yes, it is "," or ";" separator ?
I've built the following function :
Function test(Ref As String) As Variant
Dim creationdetableau() As String
Dim cpt As Integer
cpt = 0
For i = 1 To 35500
If Ref = ThisWorkbook.Worksheets("CashFlows").Cells(i, "A").Value Then
ReDim Preserve creationdetableau(cpt)
creationdetableau(cpt) = ThisWorkbook.Worksheets("CashFlows").Cells(i, "B").Value
cpt = cpt + 1
Debug.Print i
End If
Next i
test = creationdetableau
End Function
If I call
=test(A2)
In an excel sheet, would it print the string in ? If No how would you do ?
What Am I trying to do :
I try to use this formula
=CfDur(E2;P2;CashFlows!B2:B100;CashFlows!J2:J100)
But my goal is to replace this CashFlows!B2:B100 by a custom vba function called test
=CfDur(E2;P2;test(A2);CashFlows!J2:J100)
CashFlows!B2:B100 contains date :
to conclude :
=CfDur(E2;P2;test(A2);CashFlows!J2:J100)
does not work
They are not "stored" as a String, but as distinct elements that can be indexed or retrieved by iteration. For example:
Sub Dorian()
Dim Arr(0 To 2) As String, i As Long
Arr(0) = "A"
Arr(1) = "B"
Arr(2) = "C"
For i = 0 To 2
MsgBox Arr(i)
Next i
For Each a In Arr
MsgBox a
Next a
End Sub
NOTE:
The elements of an array can be numbers or Pictures or Ranges or most other types of Objects.
The receiving function will read the passed variable according to the data type of the array used to pass it:
Dim arrInt() as Integer
Dim arrStr() As String
Note: Dim arrVar as Variant and Dim arrVar() as Variant will yield the same result.
The built in Array method expects variant data.
A variant parameter will accept almost any data type passed to it, provided it can be parsed, so for max compatibility you could use variant in your function parameters.
For performance or safety use specific data types instead and ensure the arrays used are of the appropriate type.
You can make an array from all basic data types and many object types too. If you can't find an array type to suit use System.Collections.ArrayList,
Related
I am having problems running a VBA function on my Excel for Mac.
I want to process a series of strings to remove any duplicate characters in the strings. For example: column 1 shows the original strings while column 2 has removed any duplicate characters.
|Original String | Duplicate Characters Removed
route | route
trout | trou
eater | eatr
brass | bras
seige | seig
smelt | smelt
I found some VBA code which purports to do this however it returns #VALUE! when I run it. Code is shown below:
Function RemoveDupes1(pWorkRng As Range) As String
'Updateby Extendoffice
Dim xValue As String
Dim xChar As String
Dim xOutValue As String
Set xDic = CreateObject("Scripting.Dictionary")
xValue = pWorkRng.Value
For i = 1 To VBA.Len(xValue)
xChar = VBA.Mid(xValue, i, 1)
If xDic.Exists(xChar) Then
Else
xDic(xChar) = ""
xOutValue = xOutValue & xChar
End If
Next
RemoveDupes1 = xOutValue
End Function
I call this function by entering =RemoveDupes1(A2) in cell B2 (where A2 holds the first string in my list), however I receive #VALUE! error.
I dont know if the problem is in the VBA code (others seem to have succesfully used it, but perhaps not on a Mac) or the way I am applying it (I dont really know VBA but have succesfully applied other snippets in the past). Any advice gratefully received. TIA.
As Ron wrote in the comments, there is no Scripting.Dictionary on MAC - the Dictionary is part of the Microsoft Scripting Library (scrrun.dll) and dll's (dynamic link library) doesn't exist on a Mac.
Now using a Dictionary for duplicate checking is a good idea in general because it's very fast and easy to use, however, when it comes to simple checking if a string contains a character, it's a little overkill.
The following function will copy every character of the source string into the destination string if it is not already in - the check is done by using InStr.
I have changed the parameter type to Variant and convert the content into a string using the function CStr - with that you can call the function with nearly every data type (Range, String, even Numbers or Dates).
Function RemoveDuplicateChars(param As Variant) As String
Dim sourceString As String, i As Long
sourceString = CStr(param)
For i = 1 To Len(sourceString)
Dim xChar As String
xChar = Mid(sourceString, i, 1)
If InStr(RemoveDuplicateChars, xChar) = 0 Then
RemoveDuplicateChars = RemoveDuplicateChars & xChar
End If
Next
End Function
Update: Just for completeness, I added an optional 2nd parameter so that the function can be used case insensitiv (If the string contains an uppercase and a lowercase character, only one of them is copied)
Function RemoveDuplicateChars(param As Variant, Optional IgnoreCase As Boolean = False) As String
Dim sourceString As String, i As Long
sourceString = CStr(param)
For i = 1 To Len(sourceString)
Dim xChar As String
xChar = Mid(sourceString, i, 1)
Dim compareMethod As Long
compareMethod = IIf(IgnoreCase, vbTextCompare, vbBinaryCompare)
If InStr(1, RemoveDuplicateChars, xChar, compareMethod) = 0 Then
RemoveDuplicateChars = RemoveDuplicateChars & xChar
End If
Next
End Function
I used VBA to write a homemade function to calculate the length of a vector (Euclidean distance), and currently it works for any range selected for function parameter.
Function VectorLength(Numbers1 As Range) As Double
Dim i As Range
Dim val As Double
For Each i In Numbers1
val = val + i * i
Next
VectorLength = Math.Sqr(val)
End Function
Here, I want to generalize it to work with separate cells (non-connected cells, e.g. A3:A5, A7, A9) or double type values (e.g. 3.02, 1E-5, A3:A5).
But, for example, when I try to change to separate cells where I need to set the number of input parameters as variant, I used ParamArray. However, the code below comes to an error when running.
Function VectorLength2(ParamArray Numbers2() As Variant) As Double
Dim j As Long
Dim k As Variant
For j = LBound(Numbers2) To UBound(Numbers2)
For Each k In Numbers2(j)
val = val + k * k
Next k
Next j
VectorLength = Math.Sqr(val)
End Function
Well, my main aim is to imitate excel pre-defined SUM or AVERAGE function, with the input parameters being any of the double type values, separate cells or range.
Many thanks!
The code below will point you towards a solution. You basic problem is that you wish to have multiple different type allowed in your paramarray. VBA can suport this through the Variant type BUT you have the responsibility of determining what happens for each different type.
The code below is written to differentiate three different type groups.
An object (which is presumed to be an excel range)
An Array, to allow Range(xxx).value to be used as a parameter
Anything that is a single value that can be evaluated as a number
Option Explicit
Function VectorLength(ParamArray ipNumbers() As Variant) As Double
Dim myResult As Double
Dim myVal As Variant
Dim myItem As Variant
For Each myItem In ipNumbers
Select Case True
' It is assumed that any object passed to the method is an excel.range
Case VBA.IsObject(myItem)
For Each myVal In myItem
myResult = myResult + CDbl(myVal) * CDbl(myVal)
Next
Case VBA.IsArray(myItem)
' the code is the same as for ISObjewct but
' a separate case statement is used in case
' you want to do something different with an array item
For Each myVal In myItem
myResult = myResult + CDbl(myVal) * CDbl(myVal)
Next
Case VBA.IsNumeric(myItem)
myResult = myResult + CDbl(myItem) * CDbl(myItem)
Case Else
Err.Raise 17, "VectorLenth", "A non numeric item was found: '" & CStr(myItem) & ")"
End Select
Next
VectorLength = vba.math.sqr(myResult)
End Function
The above code compiles without error and has no notable Rubberduck code inspections, but otherwise has not been tested.
I am struggling with the split function in VBA. Maybe I did something wrong with the declarations despite trying to switch around with Dim as Variant and Dim as String.
My code looks as following:
'Split the txtString variable at every "|" and add every split string item to an array split_sText
Dim txtString as String
.
.
.
Dim split_sText() As String
split_sText() = Split(txtString, "|")
Pick the first part of respective item out of that array and place it at the right cell
Sheets(Table1).Cells(1, 1) = Split(split_sText(15), "_")'
split_sText(15) looks like this: "ABC_1234". Of that string I only want to get the "ABC".
The error occurs at the last line because of "runtime error 13 not matching types", which is strange, since I've declared the variables as strings.
Thank you for your help!
I believe you are looking at the wrong place for your error.
Sheets(Table1) is the source of the error.
The Index argument for the Sheets object needs to be either a number, or a sheet name. Most likely, Table1 is the CodeName. The CodeName of an object returns the actual object.
So if Table1 is the CodeName, then change to
Table1.Cells(1,1) = ...
If Table1 happened to be the worksheet name, then it would be
Sheets("Table1").Cells(1,1) = ...
If you look at the Project Explorer window in the VBE, under the Microsoft Excel Objects for your project you will see one or more worksheet objects. The CodeName is the first name of the worksheet; the worksheet Name is the name in parentheses.
There are other issues with how you are splitting the string, but your code should work with a proper Sheets reference.
Dim Sp() As String declares an array of string type with an undefined number of elements.
Sp = Split("a_b_c_", "_") assigns an array to the variable already declared, defining also the number of elements, 0-based.
Debug.Print Sp(2) prints the 3rd element to the Immediate Pane.
Cells(1, 1).Value = Sp(2) assigns the same value to a cell.
The Split function returns an array of string type. However, the following will also work.
Dim Sp As Variant
Sp = Split("a_b_c_", "_")
Cells(1, 1).Value = Sp(2)
Observe that the variant is not declared as an array because the variant c an be anything by nature. Dim Sp() As Variant will cause Sp = Split("a_b_c_", "_") to fail.
As one final twist, you can also avoid the use of Sp with the code below.
Cells(1, 1).Value = Split("a_b_c_", "_")(2)
split_sText(15) looks like this: "ABC_1234". Of that string I only want to get the "ABC".
If there's one under score then there'd be a two item array, and you can only assign one array item to a cell:
Dim split_sText() As String
split_sText() = Split(txtString, "|")
Dim split_sTextNested() As String
split_sTextNested = Split(split_sText(15), "_")
Sheets(Table1).Cells(1, 1) = split_sTextNested(0)
I have a spread sheet with multiple dynamic named ranges such as HR_B1, to HR_B10 etc.
I am trying to create a function that will find the minimum value from whichever ranges are inserted into the function, i.e. the user function will input two values into the function say 3 and 6 and it will find the minimum value over ranges HR_B3 to HR_B6.
I have created a array and for loop that stores the names of the named ranges in the array.
However I cannot get the WorksheetFunction.Min code to read the contents of the array as named ranges and output the min value.
My code is:
Public Function HR_Min_Range(minval As Integer, maxval As Integer) As Variant
Dim fullrange() As Variant
Dim total_birds As Integer
Dim i As Long
total_birds = (maxval - minval)
ReDim fullrange(total_birds)
For i = 0 To total_birds
fullrange(i) = "HR_B" & (i + minval)
Next i
HR_Min_Range = WorksheetFunction.Min(Sheets("HR_Depths").Range(fullrange))
End Function
Try it as,
Option Explicit
Public Function HR_Min_Range(minval As Integer, maxval As Integer) As Variant
Dim i As Long
HR_Min_Range = Worksheets("HR_Depths").Range("HR_B" & minval).Cells(1)
For i = minval To maxval
HR_Min_Range = Application.Min(HR_Min_Range, Worksheets("HR_Depths").Range("HR_B" & i))
Next i
End Function
The correct syntax is like this:
Worksheetfunction.min([named])
or
WorksheetFunction.min(activesheet.range("named"))
or
WorksheetFunction.min(tabelle1.Range("named"))
or
Worksheetfunction.Min(worksheets("Tabelle1").range("named"))
or
Worksheetfunction.Min(tabelle1.[named])
or
Worksheetfunction.Min(worksheets("Tabelle1").[named])
Feel free to pick anything :) That's in case that the WorkSheet is with caption and code name Tabelle1 and the named range is named named.
In you case, you are not putting fullrange in parenthesis or in [. Any of these two options should work.
I want to have a Jagged Array with two layers: 1) being a number, 2) being an Array of Strings so I can easily reference an array based off an index value. The point of this is to get the content of a text file so that each index in the jagged array is a row of the text file (layer 1 = index, layer 2 = row). These rows are populated by strings of course.
Without any adjustments, the text file has unwanted space in between strings in each row. I want the Array of strings to not include any of the wasted space ("Hello how are you " --> ["hello","how","are","you"]).
I do this though the Trim function and the Split function. Trim removes all but the delimiting spaces; Split produces the Array of Strings I want for that line. My issue is getting the array into the Jagged Array as well as creating an array without knowing its length ahead of time, as I have not Split the row of text yet.
Below is my code. When I use a variant instead of a String as my second layer I get another error which I cant seem to solve. Note: the string array which contains the text file information is tempString()
*UPDATED CODE:
so you can test this, use tempString = (" test tempstring ", "", " test test test", " "," test ", "")
Private Sub createGCStruct(ByRef tempString() As String)
' note many parameters are not included.
' also, this would be a function producing a structure, but for now I just need this to properly create a
' jagged array.
' set variables
Dim tempString2() As String
ReDim tempString2(UBound(tempString()))
Dim j As Integer
Dim jaggArray() As Variant '*****outer layer of the jagged array*****
ReDim jaggArray(UBound(tempString()) + 1)
Dim stringST() As String '*****inner layer of the jagged array*****
Dim tempString4() As String
' set initial values of structure
' ...more code...
' capture structure information from textfile array
' A) remove unnecessary spaces from existing Array
For j = LBound(tempString()) To UBound(tempString())
' check to see if line is zero length string
If tempString(j) = "" Then
' what you don't see are my commented out, futile attempts at
' solving this problem; just know that they exist
Erase stringST
stringST = tempString(j)
jaggArray(j + 1) = stringST
' trim excesive spacing
Else
tempString2(j) = Trim(tempString(j))
Erase stringST
stringST = Split(tempString2(j), " ")
jaggArray(j + 1) = stringST
End If
Next j
'Below is me testing to see if this works'
tempString4 = jaggArray(1)
MsgBox tempString4(0), vbExclamation, "test"
' B) Add sections from array to structure
'... more code...
End Sub
I can see that the "Can't assign to array" error occurs in the part of your code where you attempt to deal with the Split function's weird bugfeature behaviour whereby splitting an empty string Split("") returns a String(0 to -1) array that is utterly unusable.
The "Can't assign to array" error is caused by this line:
stringST = tempString(j)
The thing on the left, stringST, is an array. The thing on the right, tempString(j), isn't. (It's one element from the tempString array.) You can't assign a non-array to an array, hence the error.
What you can do is define an array that contains a single element, an empty string:
Dim emptyStringArrayPlaceholder() As String
ReDim emptyStringArrayPlaceholder(0 To 0)
And then use that as placeholder for empty strings:
stringST = emptyStringArrayPlaceholder
Here's how I would clean up your code:
Private Sub createGCStruct(ByRef tempString() As String)
Dim jaggArray() As Variant '*****outer layer of the jagged array*****
jaggArray = splitStringArrayElements(tempString)
'Below is me testing to see if this works'
tempString4 = jaggArray(1)
MsgBox tempString4(0), vbExclamation, "test"
' B) Add sections from array to structure
'... more code...
End Sub
where I make use of this function:
Private Function splitStringArrayElements(tempString() As String) As Variant()
Dim j As Long
Dim trimmedString As String
Dim jaggArray() As Variant
ReDim jaggArray(LBound(tempString()) To UBound(tempString()))
Dim emptyStringArrayPlaceholder() As String
ReDim emptyStringArrayPlaceholder(0 To 0)
For j = LBound(tempString()) To UBound(tempString())
trimmedString = Trim(tempString(j))
If trimmedString = "" Then
jaggArray(j) = emptyStringArrayPlaceholder
Else
jaggArray(j) = Split(trimmedString, " ")
End If
Next j
splitStringElements = jaggArray
End Function