Data Type of Named Range - vba

I have a named range in Excel called StartDates for instance. The named range only contains dates. Example code below:
Sub test2()
Dim dat_readdates() As Date
dat_readdates = Range("StartDates").Value
End Sub
This always results in the proverbial Run-time error '13': Type mismatch.
A simple change and it works fine.
Sub test2()
Dim var_readdates() As Variant
var_readdates = Range("StartDates").Value
End Sub
Is there a known rule that data must be read in as variant data type? Clearly for speed I would like to use the data type most appropriate to the data being read.
Do I need to read it as variant and then use cDate to convert to a another variable of date type? Can I do a mass conversion such as:
dat_readdates = cDate(var_readdates)

No, unfortunately. Values are fetched from Excel as Variants. When fetching a single cell, you can directly convert it to the appropriate type, i.e.
Dim x as Long
x = myCell.Value
In the code above, first myCell.Value is fetched as a Variant, then converted by VBA to long (to execute the assignment) if the conversion is possible, otherwise run-time error.
With multiple-cell ranges, it's more tricky. The range.value returns a Variant Array. Unfortunately, VBA does not provide automatic conversion of a Variant array into a typed array. Therefore the following wont work:
Dim x() as Long
x = myRange.Value
VBA does not allow conversion of arrays like with simple variables; even if the programmer is sure that all the variants in the array are of the desired type (i.e. Long in the example above).
To emphasize, forget about excel; the following wont work in VBA:
Dim varArray() ' a variant array
varArray = Array(1, 2, 3, 4, 5)
Dim longArray() as Long
longArray = varArray ' <-- type mismatch. No conversion from variant array to typed array.

Related

VBA: Using Array Formula

I want to find a row number based on two criteria, in column C and E. My data looks like this:
I have googled my problem and using the Match function as an array formula works for this (worked when I used it in Excel, not VBA), but I can't figure out how to make it an array formula in VBA. Different solutions, be it using "[]" or .Evaluate didn't work for me (maybe that was my mistake, though). So how would I modify this code to get the result I want:
Sub Test1()
Dim rowDB As Long
Dim wsDB As Worksheet
Set wsDB = ActiveSheet
rowDB = WorksheetFunction.Match(CDate("30.06.2020") & "EX0500-0001", wsDB.Range("C7:C366") & wsDB.Range("E7:E366"))
End Sub
The error I get is "error 13: type mismatch", so I'm not sure if there's another issue here or just the lack of an array formula.
I played with this for a bit and found several problems:
It seems that CDate() doesn't like "30.06.2020" as input and gets a type error. It seems to be happy with "30-06-2020" so maybe use that format instead or just search for string "30.06.2020" instead? This should be ok as long as all of the date formats are consistent.
The WorksheetFunction.Match() second parameter must be a contiguous range and yours is not. Also I don't think the expression wsDB.Range("C7:C366") & wsDB.Range("E7:E366") makes sense; if you want to combine ranges use the Union() function. But this will not work here because as mentioned the range is not contigous.
I don't think it is possible to use WorksheetFunction.Match() to search for multiple values, so you might have to search for the date in coulmn C and the string in column E separately.
Here is some vba I got working for just searching for one value:
Sub Test4()
Dim rowDB As Long
Dim wsDB As Worksheet
Set wsDB = ActiveSheet
rowDB = WorksheetFunction.Match("30.06.2020", wsDB.Range("C7:C366"))
Debug.Print rowDB
End Sub
Also, If a match is not found, it will get a "Application-defined or object-defined error" so you will need to implement some error handling.

(VBA) when to use .cell(1,1) or .cell(1,1).value reference

In my macros when I'm referencing to a value found in a cell, I've been inconsistently using either .cell(r,c) or .cell(r,c).value.
dim c as variant
c = sheet1.cell(1,1) 'or sheet1.cell(1,1).value
Are both references correct or is there a preferred way?
.Value is the default property of the range object. Therefore, when you assign something like this:
myVar = myRange
it is equivalent to myVar = myRange.Value, because you are assigning a variable not an Object.
However, if you use Set, like this:
Set myObj = myRange
You would be assigning an object reference. The keyword Set tells VBA that you are assigning an object reference. The absence of Set makes VBA conclude that you implicitly want to get the .value, the default property of the range.
It is a good programming practice to use .value explicitly, for two reasons:
1- It makes code more readable, because the reader does not have to guess what is going on implicitly
2- With the advent of VB.net, the keyword Set has disappeared; the syntax for assigning an object or a normal variable becomes the same. For this reason, the default property idiom has disappeared with VB.net. Therefore, good practice is to use .value in VBA as well, because it makes an eventual porting of your code to VB.net much easier.
I always use .Value2 to get the value of a cell (or an array of vables from a range). If I don't use .Value2 it means I want a reference to the Range objects and not the value. For example
Dim r as Range
Set r = Range("A1")
Dim x as Double, i as Integer
x = r.Offset(1,0).Value2
Dim vals() as Variant
vals = r.Offset(1,0).Resize(10,1).Value2
For i=1 to 10
vals(i,1) = CDbl(i)/10
Next i
r.Offset(1,0).Resize(10,1).Value2 = vals
In addition I dont use the Cell() method because the location of cells may change in the future. I use named ranges with the .Offset() and .Resize() methods to set the range of cells I want read or write values to. So in the above code I would never use Range("A1") but something like Range("my_table") with a named range "my_table" defined on the top left cell of where I interact with.
There is a little known shortcut to get the value of a cell with the [] notation
Dim x as Double
x = [A2]
// This is the same as x = Range("A2").Value2

VBA User Form Range to Variable- Syntax Issue

I've created a VBA Userform in Excel, where the user selects several ranges. Within the user form, I have input validation through a series of If then MsgBox statements.
As part of this, I need to take the inputted range and use it as a variable.
Assuming that the range is Me.ActDurations, I tried to use this:
dim ActDur as range
set ActDur = Me.ActDurations
I've also tried:
set ActDur = Me.ActDurations.Value
And that doesn't work either. What is the proper syntax for this? Using the first type gives me a type mismatch error.
The .Value property of RefEdit returns a string. To use it as a range, you should use the string as a range name. Sample code below.
Dim address as String
Dim targetRange As Range
address = RefEdit1.Value 'String returned by the selected range using RefEdit.
Set targetRange = Range(address)
'Do some code here.
Modify as necessary for your code. ;)

Excel VBA Cell value rounded when passed to an array

I'm hoping this has a simple solution. I need to take values in from a worksheet, perform calculations and insert them into a hidden worksheet (to be uploaded later into a database). I don't normally program in VBA, so I'm not sure what I'm doing wrong. My problem is this: when I copy data from the worksheet into the array, they are rounded like an integer when I've specified long. The following code snippet will give you an idea of the problem. Am I initializing the array wrong?
Dim ThisWS As Worksheet
Set ThisWS = Excel.ActiveWorkbook.Worksheets("BchSheet")
Dim BTW() As Long 'Beaker Tare Weight
ReDim Preserve BTW(Samples)
BTW(1) = ThisWS.Cells(18, 6).Value 'Value in cell is 98.7036
MsgBox (ThisWS.Cells(18, 6).Value) 'Returns 98.7036
MsgBox (BTW(1)) 'Returns 99
The Long data type, like Integer, only holds whole numbers.
Use the Double data type to store decimals, or the Currency data type if you are working with calculations where fixed-point is necessary or you don't want to deal with floating-point numbers.

Pass a range into a custom function from within a cell

Hi I'm using VBA in Excel and need to pass in the values from two ranges into a custom function from within a cell's formula. The function looks like this:
Public Function multByElement(range1 As String, range2 As String) As Variant
Dim arr1() As Variant, arr2() As Variant
arr1 = Range(range1).value
arr2 = Range(range2).value
If UBound(arr1) = UBound(arr2) Then
Dim arrayA() As Variant
ReDim arrayA(LBound(arr1) To UBound(arr1))
For i = LBound(arr1) To UBound(arr1)
arrayA(i) = arr1(i) * arr2(i)
Next i
multByElement = arrayA
End If
End Function
As you can see, I'm trying to pass the string representation of the ranges. In the debugger I can see that they are properly passed in and the first visible problem occurs when it tries to read arr1(i) and shows as "subscript out of range". I have also tried passing in the range itself (ie range1 as Range...) but with no success.
My best suspicion was that it has to do with the Active Sheet since it was called from a different sheet from the one with the formula (the sheet name is part of the string) but that was dispelled since I tried it both from within the same sheet and by specifying the sheet in the code.
BTW, the formula in the cell looks like this:
=AVERAGE(multByElement("A1:A3","B1:B3"))
or
=AVERAGE(multByElement("My Sheet1!A1:A3","My Sheet1!B1:B3"))
for when I call it from a different sheet.
First, see the comment Remou left, since that's really what you should be doing here. You shouldn't need VBA at all to get an element-wise multiplication of two arrays.
Second, if you want to work with Ranges, you can do that by declaring your function arguments to be of type Range. So you could have
Public Function multByElement(range1 As Range, range2 As Range)
and not need to resolve strings to range references yourself. Using strings prevents Excel from updating references as things get moved around in your worksheet.
Finally, the reason why your function fails the way it does is because the array you get from taking the 'Value' of a multi-cell Range is two-dimensional, and you'd need to acces its elements with two indices. Since it looks like you're intending to (element-wise) multiply two vectors, you would do either
arrayA(i) = arr1(i,1) * arr2(i,1)
or
arrayA(i) = arr1(1,i) * arr2(1,i)
depending on what orientation you expected from your input. (Note that if you do this with VBA, orientation of what is conceptually a 1-D array matters, but if you follow Remou's advice above, Excel will do the right thing regardless of whether you pass in rows or columns, or range references or array literals.)
As an epilogue, it also looks like you're not using 'Option Explicit'. Google around for some rants on why you probably always want to do this.