In VBA, how do I convert An array from one type to another?, in my case i want to convert an array of type "String" to type "variant" because i have a function parameter that needs an array of that type.
here is an example code,
Sub test_highlighfind()
Dim Rng As Range: Set Rng = ThisDocument.Range.Paragraphs(6).Range
Dim arr() As String: arr = Split(Rng.Text)
Call highlightWordsUsingFind(arr, ThisDocument, 7)
End Sub
Sub highlightWordsUsingFind(ByRef arr() As Variant, ByRef doc As Document, _
Optional ByVal HighlightColor As Integer = 6)
Dim i As Long, SearchRange As Range
Set SearchRange = doc.Range
With SearchRange.Find
.Format = True
.MatchCase = False
.MatchWholeWord = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Forward = True
.Wrap = wdFindContinue
.ClearFormatting
For i = LBound(arr) To UBound(arr)
.Text = arr(i)
.Execute 'Execute// Runs the find operation. Returns True if the find operation is successful
SearchRange.HighlightColorIndex = HighlightColor
Next
End With
End Sub
i know i can change the parameter type to "string" ByRef arr() As String but i have other functions that return an array of type "variant" and i need the output to the function above
Remove the parentheses in your declaration.
Sub highlightWordsUsingFind(ByRef arr As Variant, ...)
Then you can pass arr() directly. It will be wrapped into a Variant/Array that will refer to the original array (so no copying will happen).
Since you wrote the highlightWordsUsingFind sub, the easiest way is to change the parameter to Variant as already mentioned by GSerg. It might be a good idea to ensure the parameter is actually an array with the IsArray function. Here are some other conversion examples.
Another option is to create a new Variant array:
Function StringToVariantArray(ByRef arr() As String) As Variant()
Dim index As Integer
Dim result() As Variant
Redim result(LBound(arr) To UBound(arr))
For index = LBound(arr) To UBound(arr)
result(index) = arr(index)
Next
StringToVariantArray = result
End Function
Sub test_highlighfind()
Dim Rng As Range
Dim varr() As Variant
Set Rng = ThisDocument.Range.Paragraphs(6).Range
varr = StringToVariantArray(Split(Rng.Text))
Call highlightWordsUsingFind(varr, ThisDocument, 7)
End Sub
Related
I need to make an array of numbers from "n" to "n2" to use it in an IF function like
If ArrayContainsItem([transpose(row(320:420))], WidthArray) Then
'Do stuff
End If
And I am wondering, how do you write that first array (which must be written inside that line directly and not dimensioned prior)? It returns an error "array or user-defined type expected"
I got it working with
dim arr() as variant
arr = [transpose(row(320:420))]
'Main function
If ArrayContainsItem(arr, WidthArray) Then
'Do stuff
End If
End Sub
But I need it to be made up directly inside the if function line like in the first code example.
The function I am using looks like (can't be altered in any way)
Function ArrayContainsItem(ArrayBig() As Variant, ArraySmall() As Variant) As Boolean
'Declare variables
Dim iOption As Long
'Set variables
ArrayContainsItem = False
'Main function
For iOption = 2 To UBound(ArraySmall)
For Each Item In ArrayBig
If CStr(Item) = ArraySmall(iOption) Then
ArrayContainsItem = True
End If
Next Item
ArrayContainsItem = False
Next iOption
End Function
Changing the function to
Function ArrayContainsItem(ArrayBig As Variant, ArraySmall() As Variant)
returns error 2015 on ArrayBig and item is empty.
Would, someone, please help me figure this one out?
Reproducable example
Sub ArrayTesting()
'Old variables
Dim WS_MS As Worksheet
Set WS_MS = ThisWorkbook.Worksheets("Machine Specification")
Dim LowerFilmWidthArray() As Variant
Dim CurrentParameter As Range
Dim ParametersColumn As Long
ParametersColumn = 2
Dim LastColumn As Long
LastColumn = 4
'Width
Set CurrentParameter = WS_MS.Cells.Find("Width", lookat:=xlWhole)
WidthArray = Application.Transpose(Application.Transpose(WS_MS.Range(Cells(CurrentParameter.Row, ParametersColumn), Cells(CurrentParameter.Row, LastColumn)).Value))
'Main function
If ArrayContainsItem([transpose(row320:420)], WidthArray) Then
End If
End Sub
Change your function declaration:
Function ArrayContainsItem(ArrayBig() As Variant, ArraySmall() As Variant) As Boolean
to
Function ArrayContainsItem(ArrayBig As Variant, ArraySmall() As Variant) As Boolean
Sample:
Sub foo()
Dim arraySmall(1 To 3) As Variant
arraySmall(1) = "foo"
arraySmall(2) = "bar"
arraySmall(3) = "baz"
Debug.Print ArrayContainsItem([transpose(row(320:420))], arraySmall) ' Returns False
arraySmall(1) = "1"
arraySmall(2) = "2"
arraySmall(3) = "420"
Debug.Print ArrayContainsItem([transpose(row(320:420))], arraySmall) ' Returns True
End Sub
Function ArrayContainsItem(ArrayBig As Variant, arraySmall() As Variant) As Boolean
'Declare variables
Dim iOption As Long
'Main function
For iOption = 2 To UBound(arraySmall)
Dim i As Long
For i = LBound(ArrayBig) To UBound(ArrayBig)
If CStr(ArrayBig(i)) = arraySmall(iOption) Then
ArrayContainsItem = True
Exit Function '<-- add this
End If
Next
Next iOption
End Function
My goal is to check in one column if each cell contains (besides other values) a letter from my array.
The cells look something like "123A".
My array contains values A, C, D, X, Y, Z.
Both just example values.
Note my if-statement should be true if the cell contains any of the letters, so for the example it should be true.
For the cell Value "123B" it should be false (no B in array).
I found a "IsinArray" Function that appears to be working but checks for specific values.
What I would need is closer to ---> "*" & IsinArray & "*"
The function I found looks like this:
Public Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = Not IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Also my For statement to loop through the column is working (I'd say ^^)
I am also open to "creative" solutions if you have any ideas how to do it better.
Try,
Public Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
Dim s As String, i As Integer
Dim a As Variant
For i = 1 To Len(stringToBeFound)
s = Mid(stringToBeFound, i, 1)
For Each a In arr
If s = a Then
IsInArray = True
Exit Function
End If
Next a
Next i
End Function
You could swop it around
Option Explicit
Public Sub Test()
Dim testArray(), cellValue As String, rng As Range
Set rng = ThisWorkbook.Worksheets("Sheet1").Range("A1") '<== contains
testArray = Array("A", "C", "D", "X", "Y", "Z")
Debug.Print IsInArrayValue(testArray, rng)
End Sub
Public Function IsInArrayValue(ByVal testArray As Variant, ByVal rng As Range) As Variant
Dim i As Long, testString As String
testString = rng.Text
If rng.Cells.Count > 1 Then
IsInArrayValue = CVErr(xlErrNA)
Exit Function
End If
For i = LBound(testArray) To UBound(testArray)
If InStr(testString, testArray(i)) > 0 Then
IsInArrayValue = True
Exit Function
End If
Next
IsInArrayValue = False
End Function
If using as an UDF you could either pass the array in as shown above or if array doesn't change you could move into the function itself. Personally, I prefer passing the array as an argument to the function as more flexible. I can't work out where your row to copy is coming from. Your comment posted as an answer uses a j variable that doesn't appear to be involved in the shown loop and the row is copied from another sheet. So below will not work directly but gives you a framework.
Public Function IsInArrayValue(ByVal rng As Range) As Variant
Dim i As Long, testString As String, testArray()
testArray = Array("A", "C", "D", "X", "Y", "Z")
testString = rng.Text
If rng.Cells.Count > 1 Then
IsInArrayValue CVErr(xlErrNA)
Exit Function
End If
For i = LBound(testArray) To UBound(testArray)
If InStr(testString, testArray(i)) > 0 Then
IsInArrayValue = True
Exit Function
End If
Next
IsInArrayValue = False
End Function
Call:
The comment below is looks like a new question but you probably want something like:
Dim loopRange As Range, rng As Range
With ThisWorkbook.Worksheets("Filter")
Set loopRange = .Range(.Cells(1, VarNutzerSpalte), .Cells(VarAnzahlZeilen, VarNutzerSpalte))
End With
For Each rng In loopRange
If IsInArrayValue(ArrAuswahlNutzer, rng) Then
rng.EntireRow.Copy '<= use Union to gather range if all being pasted in a block somewhere
End If
Next
A union version might looks like:
Dim loopRange As Range, rng As Range, unionRng As Range
With ThisWorkbook.Worksheets("Filter")
Set loopRange = .Range(.Cells(1, VarNutzerSpalte), .Cells(VarAnzahlZeilen, VarNutzerSpalte))
End With
For Each rng In loopRange
If IsInArrayValue(ArrAuswahlNutzer, rng) Then
If Not unionRng Is Nothing Then
Set unionRng = Union(unionRng, rng)
End If
Set unionRng = rng '<= use Union to gather range if all being pasted in a block somewhere
End If
Next
If Not unionRng Is Nothing Then
unionRng.EntireRow.Copy 'destination for paste
End If
Sorry to answer my own question (corrections/feedback to this solution is welcome of course)
I tried it like this and i guess it should work (cant really test because other parts of my makro arent working)
It is kinda unecessary complicated and maybe slow but id say it could work:
For i = 1 To VarAnzahlZeilen
Set rng = Worksheets("Filter").Range(Cells(i, VarNutzerSpalte), Cells(i, VarNutzerSpalte))
If IsInArrayValue(ArrAuswahlNutzer, rng) Then
Worksheets("Import").Rows(j).Copy
Worksheets("Filter").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
End If
Next i
Uses this function by QHarr (with only the array name changed)
Public Function IsInArrayValue(ByVal testArray As Variant, ByVal rng As Range) As Variant
Dim i As Long, testString As String
testString = rng.Text
If rng.Cells.Count > 1 Then
IsInArrayValue = CVErr(xlErrNA)
Exit Function
End If
For i = LBound(testArray) To UBound(testArray)
If InStr(testString, testArray(i)) > 0 Then
IsInArrayValue = True
Exit Function
End If
Next
IsInArrayValue = False
End Function
Thanks alot # QHarr and also # Dy.Lee !
Let's say I have something like "1-34-52", I want to split them into an array, however, while Test1 works, it gives me an String() type array. How do I put the numbers in "1-34-52" into a Long() type array? Can I redim type of an array in VBA?
Sub Test1()
Dim arr As Variant
arr = Split("1-34-52", "-")
Debug.Print TypeName(arr), TypeName(arr(0))
End Sub
Sub Test2()
Dim arr() As Long
arr = Split("1-34-52") 'You get type mismatch error
End Sub
You can Redim an array of Variants. Since Variants can hold integer values, there is no problem:
Sub dural()
ary = Split("1-34-52", "-")
ReDim lary(0 To UBound(ary))
For i = 0 To UBound(ary)
lary(i) = CLng(ary(i))
Next i
End Sub
Note:
Sub dural()
ary = Split("1-34-52", "-")
Dim lary() As Long
ReDim lary(0 To UBound(ary))
For i = 0 To UBound(ary)
lary(i) = CLng(ary(i))
Next i
End Sub
will also work.
You can loop through the array and populate a new one:
Sub Test1()
Dim arr As Variant, LongArr() As Long, X As Long
arr = Split("1-34-52", "-")
ReDim LongArr(UBound(arr))
For X = LBound(arr) To UBound(arr)
LongArr(X) = CLng(arr(X))
Next
Debug.Print TypeName(arr), TypeName(arr(0))
Debug.Print TypeName(LongArr), TypeName(LongArr(0))
End Sub
I have function that returns me list of current sheets:
Function getListOfSheetsW() As Variant
Dim i As Integer
Dim sheetNames() As Variant
ReDim sheetNames(1 To Sheets.Count)
For i = 1 To Sheets.Count
sheetNames(i) = Sheets(i).name
Next i
getListOfSheetsW = sheetNames
End Function
Then I have function which returns TRUE or FALSE depending on if needle is in haystack or not.
Function IsInArray2(ByVal needle As String, haystack() As String) As Boolean
Dim element As Variant
For Each element In haystack
If element = needle Then
IsInArray = True
Exit Function
End If
Next element
IsInArray = False
End Function
My goal is to create new subroutine which will first check if sheet with given name already exist and if not then create new one. I've tried following:
Sub CreateNewSheet(ByVal dstWSheetName As String)
Dim srcWSheetName As String
' Dim sheetNames() As String
Dim sheetNames() As Variant
sheetNames = getListOfSheetsW()
Dim sheetCount As Integer
If IsInArray2(dstWSheetName, sheetNames) Then
MsgBox "Sheet with following name: " & dstWSheetName & " already exists"
Else
srcWSheetName = ActiveSheet.name
sheetCount = Sheets.Count
' CREATE NEW SHEET
' Worksheets(dstWsheetName).Delete
Sheets.Add.name = dstWSheetName
' Q: why 6 instead of 5
' Worksheets("Test").Move after:=Worksheets("Sheet5")
Worksheets(dstWSheetName).Move After:=Worksheets(sheetCount + 1)
' SWITCH TO SRC SHEET
Worksheets(srcWSheetName).Activate
End If
End Sub
I'm calling it this way:
Sub CallCreateNewSheet()
Call CreateNewSheet("test")
End Sub
I guess the problem is with Dim sheetNames() As String or Dim sheetNames() As Variant.
When I use Dim sheetNames() As String I get
Run-time error '13': Type mismatch
When I use Dim sheetNames() As Variant I get:
Compile error: Type mismatch: array or user-defined type expected
I had similar problem before but defining sheetNames as array did not helped here. What is the problem and what does the two different errors mean?
You will avoid all these problems if you switch from typed arrays to variant-arrays.
In your first function, delete this line:
Dim sheetNames() As Variant
Change the definition line of your 2nd function from this:
Function IsInArray2(ByVal needle As String, haystack() As String) As Boolean
...to this:
Function IsInArray2(ByVal needle As String, haystack) As Boolean
In your sub, change this line:
Dim sheetNames() As Variant
...to this:
Dim sheetNames
How about a new script like:
Sub NewSheetByName(SName as String)
Dim oldSheet as Object
For Each oldSheed in ThisWorkbook.Sheets
if oldSheet.Name = Sname Then
MsgBox "Sheet with following name: " & SName & " already exists"
Exit Sub
End If
Next
oldSheet = ActiveSheet
Sheets.Add.Name = SName
ActiveSheet.Move , Worksheets(Sheets.Count)
oldSheet.Activate
End Sub
The variables has to be in sinc.
Declare the variable sheetNames in the same manner in both procedures:
Sub CreateNewSheet(ByVal dstWSheetName As String) and
Function getListOfSheetsW() As Variant
declare it as : Dim sheetNames() As String
Also note that the Function IsInArray2 always returns False.
To correct this replace IsInArray with IsInArray2 in the body of the function.
It's a good practice to always have the
Option Explicit
at the beginning of the modules.
However it'll save all the trouble to validate the existence of a worksheet, just to assign the target worksheet to a variable, it will give an error and the variable return nothing if the worksheet in not present. Try this:
Dim Wsh As Worksheet
On Error Resume Next
Set Wsh = Workbook(x).Worksheets("Test")
On Error GoTo 0
If Wsh Is Nothing Then Add Worksheet
I am tiring alter a macro by seting a range as my array
This works fine
Sub FindReplaceByArrays()
Dim FindValues As Variant
Dim ReplaceValues As Variant
Dim i As Long
FindValues = Array("Find1", "Find2", "Find3")
ReplaceValues = Array("Replace1", "Replace2", "Replace3")
Sheets("UnPivot").Select
For i = LBound(FindValues) To UBound(FindValues)
Columns("P:P").Replace FindValues(i), ReplaceValues(i), xlWhole, xlByColumns, False
Next i
End Sub
Tiring to change to the following Sub but get error "Script out of range` and
Columns("P:P").Replace FindValues(i), ReplaceValues(i), xlWhole, xlByColumns, False
is high lighted
Thanks
Sub FindReplaceByArrays2()
Dim FindValues() As Variant
Dim ReplaceValues() As Variant
Dim i As Long
Sheets("UnPivot").Select
FindValues = Range("S2:S30")
ReplaceValues = Range("T2:T30")
For i = LBound(FindValues) To UBound(FindValues)
Columns("P:P").Replace FindValues(i), ReplaceValues(i), xlWhole, xlByColumns, False
Next i
End Sub
Try this one:
Sub FindReplaceByArrays2()
Dim FindValues As Variant
Dim ReplaceValues As Variant
Dim i As Long
With Sheets("UnPivot")
FindValues = .Range("S2:S30").Value
ReplaceValues = .Range("T2:T30").Value
For i = LBound(FindValues) To UBound(FindValues)
.Columns("P:P").Replace FindValues(i, 1), ReplaceValues(i, 1), xlWhole, xlByColumns, False
Next i
End With
End Sub
Note, that I'm using
Dim FindValues As Variant instead Dim FindValues() As Variant
FindValues = .Range("S2:S30").Value - with .Value property (you can read about why you should use it here: Why am I having issues assigning a Range to an Array of Variants)
also FindValues = .Range("S2:S30").Value actually creates 2D array, that's why I'm using FindValues(i, 1). The same for ReplaceValues(i, 1)
and also: How to avoid using Select/Active statements:)