If I do Dim array As String() I seem to be able to resize that array and put anything in it on the fly. For example:
Dim PackUrls As String()
PackUrls = Split(WebRequest("http://" + sPackBaseURI, sPackBaseURIUsername, sPackBaseURIPassword), ":")
And I don't get an error and nothing weird is happening with the array.
I did this without thinking THEN i read that you have to specify the size of the array first! Why is this happening?
This is what is happening in your code
'Create an empty string array called PackUrls
Dim PackUrls As String()
'Create a new string array from the Split function
'and assign it to the variable PackUrls, replacing the old value.
PackUrls = Split(WebRequest("http://" + sPackBaseURI, _
sPackBaseURIUsername, sPackBaseURIPassword), ":")
There is no copying from the result of Split to PackUrls. The original content (which is nothing by the way) is replaced with the new content from Split. Therefore there is no need to resize the array. You could easily rewrite your code like this and it would work:
Dim PackUrls As String() = Split(WebRequest("http://" + sPackBaseURI, _
sPackBaseURIUsername, sPackBaseURIPassword), ":")
When you assign and array to var you are replacing it with the new array, no matter the size. The var now is pointing to a new data of the same type, so no error is needed.
When you assing a array to an array var you are not copying the items individually, but the variable is poiting to the new array.
Related
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
I am using an array to process the values (and texts) I have in a sheet:
Dim arr As Variant
arr = Application.ActiveSheet.UsedRange
'do stuff
Application.ActiveSheet.UsedRange = arr
However, this has a side-effect: when dumping the array back into the cells, the cells formatting is changed by Excel's default behaviour. For example, numbers stored as text which start with "0" are converted to numbers, and the "0" gets deleted. Or texts like "1-11" are converted to dates ("November 1"); and probably some others which I have not spotted yet.
If I monitor the Locals window, the strings are being preserved as strings in the array until the very moment, so it is the unloading that messes things up.
Is there a way to avoid this behavior ?
Edit: I also tried:
Application.ActiveSheet.UsedRange.Value = arr
Application.ActiveSheet.UsedRange.Value2 = arr
Application.ActiveSheet.UsedRange.text = arr
Same result for each.
You can use the valuetype option to preserve formatting etc: 11 is xlRangeValueXMLSpreadsheet
Sub CopyWithFormat()
Dim var As Variant
var = Range("A8:A11").Value(11)
Range("C8:C11").Value(11) = var
End Sub
But that will make it difficult to modify the values in the array.
So its probably simplest to loop the array adding '
Sub CopyWithFormat2()
Dim var As Variant
Dim j As Long
var = Range("A8:A11").Value
For j = 1 To UBound(var)
If VarType(var(j, 1)) = vbString Then var(j, 1) = "'" & var(j, 1)
Next j
Range("C8:C11").Value = var
End Sub
Also try
YourRangeVariable.NumberFormat = "#"
YourRange.Value=Your Array
This will convert the range to text first. Works for me when using values like 01-11 and saves having to do a loop.
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.
Is there a way in VB.NET to declare an array, and later initialize it to a known length in the code? In other words, I'm looking for the VB.NET equivalent of the following C#.NET code:
string[] dest;
// more code here
dest = new string[src.Length];
I tried this in VB, and it didn't work.
Dim dest() as string
' more code here
dest = New String(src.Length)
What am I missing?
NOTE: I can confirm that
Dim dest(src.Length) as string
works, but is not what I want, since I'm looking to separate the declaration and initialization of the array.
The syntax of VB.NET in such a case is a little different. The equivalent of
string[] dest;
// more code here
dest = new string[src.Length];
is
Dim dest As String()
' more code here
dest = New String(src.Length - 1) {}
Syntax note
When you use Visual Basic syntax to define the size of an array, you specify its highest index, not the total number of elements in the array. learn.microsoft.com
Example
These two array both have a length of 5:
C#:
string[] a = new string[5];
VB:
Dim a As String() = New String(4) {}
The normal way to do this would be to declare the array like so:-
Dim my_array() As String
and later in the code
ReDim my_array (src.Length - 1)
You can use Redim as already noted but this is the equivalent VB code to your C#
Dim dest As String()
dest = New String(src.Length - 1) {}
Try and avoid using dynamic arrays though. A generic List(Of T) is much more flexible
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