Getting full Split array into a VBA variable - arrays

Say I have a path in Range("A1") that looks like this:
/data/apps/server/
I would like to get the three elements into a variable. I've thought doing a Split() by the separator / I would get the full array:
Dim myElements()
myElements = Split(Range("A1").Value,"/")
'>>> EXPECTED: myElements is [data, apps, servers]
but I actually get a Type mismatch error on the line myElements = Split(Range("A1").Value,"/"). What does the Split function return? Does it actually return the array or it rather gives read-only access?
I would just like to get the array of the Split method without having to loop through them and build my own array, if possible of course.

Change Dim elements() to Dim elements As Variant
You need to declare it as a Variant.
Explanation:
The data in Excel cell can be anything. So use a Variant. In cases like below, you know it is a String so declare it like a String
Sub Sample()
Dim myElements() As String
Dim myString As String
myString = "aaa/bbb/ccc"
myElements = Split(myString, "/")
Debug.Print myElements(0)
Debug.Print myElements(1)
Debug.Print myElements(2)
End Sub
Split returns a String Array. You may want to see This
Edit: I have a feeling that I may confuse someone with my explanation so let me explain it a bit more.
Dim myElements() means "Declare myElements as array of Variants".
Split returns an array of Strings. Hence, the mismatch.
You can do either Dim myElements or Dim myElements as Variant or Dim myElements() as String to resolve the problem.
Here is why each one of these works:
Dim myElements and Dim myElements as Variant
Both of these means that you declare myElements as Variant. Variants are special types, which can accept anything. As such, they can accept array of strings easily. However, variants have large memory overheads and should be avoided wherever possible.
Dim myElements() as String
This means that you declare myElements as array of strings. Since this is the same type as what is returned by the Split function, it is accepted.
Ideally, if you know the return type of a function, you should specify the correct type for your variables.
So in this case, Dim myElements() as String which is the same type returned from the Split funcition.

Related

How to trim values in an array in VBA?

I got a problem I just cant fix. I have a string, want to split it at ";" (that is working) and then trim the values.
Dim cell As String
Dim objects() As String
cell = Range("X74").Text
objects= Trim(Split(cell, ";"))
I get my error on the Trim-function. I then tried the following approach:
For Each object In objects
object = Trim(object)
Debug.Print object
Next
This works, but doesnt save the trimmed value to my objects-array.
Despite naming your variables objects and object, they are an array of simple Strings resp. a simple String, and in VBA a string is not an object.
In your For Each-Loop, you are copying a string to the variable object, and no matter what you do with it, it doesn't change the content of the objects-array.
If you really need to change the content of the objects-array, use a loop like that:
Dim i As Long
For i = LBound(objects) To UBound(objects)
objects(i) = Trim(objects(i))
Debug.Print objects(i)
Next
And you should think about changing the name of your variables...
I would try to avoid vba names as variables:
Sub tst()
Dim yourcell As String, i As Long
Dim yourobjects() As String
yourcell = Range("X74").Text
yourobjects = Split(yourcell, ";")
For i = LBound(yourobjects) To UBound(yourobjects)
yourobjects(i) = Trim(yourobjects(i))
Debug.Print yourobjects(i)
Next i
End Sub

How Do You Return True If A String Contains Any Item In An Array Of String With LINQ in VB.NET

I could not find this question on stack overflow but if it is here, please let me know and I will take it down.
Using LINQ in VB.NET, how do you return True if a string contains one of the items in an array of strings?
This is this is the code in multiple lines. How do you do this in one line with LINQ in VB.NET?
Sub Main
Dim endPointTimeoutText As Array = {"endpoint timeout", "endpoint is not available"}
Dim strResult As String = "endpoint is not available sample text."
Dim booleanResult As Boolean = False
For Each item As String In endPointTimeoutText
If strResult.Contains(item) Then
booleanResult = True
Exit For
End If
Next
Console.WriteLine(booleanResult) 'Only included this for the example
End Sub
The expected result would be 'True' or 'False' depending on if the string (strResult) contained one of the values in the Array Of Strings (endPointTimeoutText)
You turn it around, mentally - don't ask "for this string X, which of these things in this array are in that string", you ask "for this array of strings, which of them are in this one string X":
Dim whichStringsArePresent = endPointTimeoutText.Where(Function(ett) strResult.Contains(ett))
Dim firstImeoutStringFound = endPointTimeoutText.FirstOrDefault(Function(ett) strResult.Contains(ett))
Dim wasATimeout = endPointTimeoutText.Any(Function(ett) strResult.Contains(ett))
etc
By the way it would make your code read more nicely if you make it so that Collections of things have plural names. Consider something more like this:
Dim wasATimeout = endPointTimeoutTexts.Any(Function(ett) strResult.Contains(ett))
It's subtle, but significant in terms of readability
Thank you Caius Jard for your help on this. I am going to post the complete program for what I'm going to use as the answer below.
I needed to use a List instead of an Array so that I could use the 'Any()' method. Thanks again Caius, I really appreciate it!
Sub Main
Dim endPointTimeoutText As String = "endpoint timeout,endpoint is not available"
Dim endPointTimeoutList As New List(Of String)
Dim strResult As String = "endpoint is not available sample text."
endPointTimeoutList = endPointTimeoutText.Split(",").ToList()
Dim areAnyStringsPresent As Boolean
areAnyStringsPresent = endPointTimeoutList.Any(Function(itemInEndPointTimeoutList) strResult.Contains(itemInEndPointTimeoutList))
Console.WriteLine(areAnyStringsPresent)
'This code produces the following output:
'True
End Sub

Type casting Strings

I am using an array list to store my strings. However when I run the program it throws an error : Conversion from type 'String()' to type 'String" is not valid. I am sure I am missing something simple. I thought an ArrayList was a list of objects and that it could be anything that I was sent to the array. e.g. Strings in, Strings out. Here is the code that I am working with.
Dim tempString As New ArrayList()
For Each s As String In tempString
Dim sAry As String() = bufString.Split(New Char() {ChrW(2)})
For i As Int16 = 1 To sAry.Length
xlCells(CurrentRow, i).Value = sAry(i - 1).ToString()
Next
Next
I also have come to understand that ArrayList has depreciated, should I be using something else?
Before jumping into your case quickly please give a look into other great answers
What's Wrong with an ArrayList?
.NET: ArrayList vs List
and you can google to know more in this regard...
For known typed collection it is not good to use ArrayLists.
For your case I would suggest generic List collection(List is much more efficient in terms of memory usage)
You can see my below test case for you:
Sub Main()
Dim tempList As New List(Of String) 'Consider List Rather ArrayList as you knw your requirement for Strings collection
tempList.Add("string1string2string3string4") 'for testing purpose I've joined "string1, string2, string3 & string4" with ChrW(2) unicode character
For Each s As String In tempList
Dim sAry As String() = s.Split(New Char() {ChrW(2)})
For i As Int16 = 1 To sAry.Length
'xlCells(CurrentRow, i).Value = sAry(i - 1).ToString()
Console.WriteLine(sAry(i - 1).ToString())
Next
Next
Console.ReadLine()
End Sub

Declaring a dynamic array not working as expected

RsProxyList.Open objDBCommand,,1,1
dim recCount:recCount = RsProxyList.RecordCount
Dim output(recCount,2)
I get an error because recCount is of wrong type. I have tried to convert it to Int but that does not work either. The following works fine:
RsProxyList.Open objDBCommand,,1,1
dim recCount:recCount = RsProxyList.RecordCount
Dim output(3,2)
How do I convert recCount to get this array declaration to work?
You need to first declare your Array as dynamic then use ReDim to set the first dimension dynamically, like this;
Dim output() 'Declare a "Dynamic Array"
ReDim output(recCount, 2) 'Set dynamic first dimension.
By specifying the dimensions like this;
Dim output(10, 2) 'Declare a "Fixed Array"
you tell VBScript to build a fixed array which does not accept variables as dimension declarations.
Dim output() versus Dim output
There seems to be some argument as to using
Dim output()
versus
Dim output
#Bond in the comments below came up with the most convincing one in my opinion for using Dim output().
Because VBScript stores all variables as Variant data type declaring an Array using Dim output is just like declaring any other variable, whereas Dim output() creates an Array (0 sized array but an array nonetheless), this means there is one significant difference as far as the VBScript Runtime is concerned, Dim output() is an Array.
What does this matter?, well it matters because functions like IsArray() will still detect the variable as an Array where as "arrays" declared using Dim output will return False.
Useful Links
#DougGlancy's answer to Redim without Dim? (vba but still relevant).
VBScript Reference - Dim Statement
VBScript Reference - VBScript Variables (specifically Array Variables sub heading).

Excel VBA: Range to String Array in 1 step

I know you can easily take a range of cells and slap them into a Variant Array but I want to work with a string array (because it's single-dimensional and takes less memory than a Variant array).
Is there any way to automatically convert a range into a string array?
Right now I am using a function that will take the range and save the values in a variant array, then convert the variant array to a string array. It works nice , but I'm looking for a way to go directly from the range to string array. Any help would be greatly appreciated.
Function RangeToArray(ByVal my_range As Range) As String()
Dim vArray As Variant
Dim sArray() As String
Dim i As Long
vArray = my_range.Value
ReDim sArray(1 To UBound(vArray))
For i = 1 To UBound(vArray)
sArray(i) = vArray(i, 1)
Next
RangeToArray = sArray()
End Function
UPDATE:
It's looking like there is no way to skip the step of throwing the data into a variable array first before converting it to a single-dimensional string array. A shame if it's true (even if it doesn't take much effort, I like to ultra-optimize so I was hoping there was a way to skip that step). I'll close the question in a few days if no solution presents itself. Thanks for the helpful comments, guys!
UPDATE2:
Answer goes to Simon who put in great effort (so did everyone else) and utlimately pointed out it's indeed impossible to go from range to string array in one shot. Thanks, everyone.
You actually can go directly from a range to an array using the functions Split, Join and a delimiter not in the text.
Assuming you have already assigned a 1D range of values as SrcRange
Dim Array() As String: Array = Split(Join(Application.Transpose(SrcRange), "#"), "#")
How about...
Public Function RangeToStringArray(theRange As Excel.Range) As String()
' Get values into a variant array
Dim variantValues As Variant
variantValues = theRange.Value
' Set up a string array for them
Dim stringValues() As String
ReDim stringValues(1 To UBound(variantValues, 1), 1 To UBound(variantValues, 2))
' Put them in there!
Dim columnCounter As Long, rowCounter As Long
For rowCounter = UBound(variantValues, 1) To 1 Step -1
For columnCounter = UBound(variantValues, 2) To 1 Step -1
stringValues(rowCounter, columnCounter) = CStr(variantValues(rowCounter, columnCounter))
Next columnCounter
Next rowCounter
' Return the string array
RangeToStringArray = stringValues
End Function
Function RangeToStringArray(myRange as range) as String()
ReDim strArray(myRange.Cells.Count - 1) As String
Dim idx As Long
Dim c As Range
For Each c In myRange
strArray(idx) = c.Text
idx = idx + 1
Next c
RangeToStringArray = strArray
End Function
If you don't mind altering the contents of the clipboard then:
COPY the range to the clipboard with the Copy method:
MyTargetRange.Copy
Copy the contents from the clipboard to a string variable (search this site or elsewhere for functions to transfer strings to/from the clipboard).
SPLIT the string into a variant array:
MyStringArray = Split(MyClipboardText, vbCrLf)
OPTIONAL: The array will have one additional blank element because there is always an additional Return (vbCrLf) at the end of the text you just copied to the clipboard. To remove simply resize the array:
Redim Preserve MyStringArray(Ubound(MyStringArray) - 1)
Very simple and quick!!!
Drawbacks are that the clipboard may change when you least expect it (during a recalculation) and that it only produces arrays of strings (not Doubles or other numerical value types).
This would be EXTREMELY HELPFUL if you are working with lots of repetitive functions (thousands) that use the same data (thousands of data points). The first time your function is called, do all the intermediate calculations on the ranges of data that you need but save the results in static variables. Also save a string copy of your input ranges via the clipboard. With each subsequent call to your function, convert the input ranges to text, again via the clipboard, and compare with the saved copy. If they are the same you may be able to bypass allot of your preliminary calculations.
Named ranges used in VBA are already arrays. So first make the range into a named range, then refer to it and delete the named range.
For example:
ThisWorkbook.Names.Add Name:="test_array", RefersTo:=Sheet1.Range("A4:B10")
a = Sheet1.Range("test_array")
ThisWorkbook.Names("test_array").Delete

Resources