VBA UBound Function - vba

I'm trying to explore UBound applications for my code in Visual Basic for Applications (VBA). Let's say I have a 4 by 2 array...(A1:B4) and I want to count the number of rows. I would think my code would be...
Function test(list) As Double
test = UBound(list)
End Function
My input is =test(A1:B4)but so far I get "#value!" error. I thought the return would be 4.
What am I doing wrong? I know how to get the number of rows using the row command but I simply want to go through the coding exercise.

List is range object not an array. Range.Value will return an array of values from a range and Range.Formula will return an array of formulas from a range.
Function test(list As Range) As Double
test = UBound(list.Value)
End Function

It seems that you have 2-dimensional array, therefore you have to provide additional parameter for UBound like:
UBound(array, dimension)
Please remember, if you get array from Excel Range than it is 1-based array.
Complete solution can look like this one:
Function testArrray(List, Dimmension)
Dim ListConverted As Variant
ListConverted = List
testArrray = UBound(ListConverted, Dimmension)
End Function
Sample call: =testArrray(G15:H20,1) produces 6 as a result which is correct.

Since you do not Dim list explicitly, it is Variant. So if you call =TEST(A1:B4) it will be a Range and not an Array. A Range does not have a UBound but does have Rows.Count.
So:
Function test(list As Range) As Double
test = list.Rows.Count
End Function
will work.
Or if you really need an Array, you could do:
Function test(list As Variant) As Double
list = list
test = UBound(list)
End Function
The list = list does the following: If list is a Range-Object then it will be implicit converted to Array since Set is not used to set an object. If list is an Array already, then it will be also an Array after that.

This is how you get the number of rows in a function.
Option Explicit
Function l_number_of_rows(rng_range As Range) As Long
l_number_of_rows = rng_range.Rows.Count
End Function
if you want the one from the dimension, this is a possible solution.
Sub test()
Dim MyArray(1 To 2, 0 To 3) As Long
MyArray(1, 0) = 10
MyArray(1, 1) = 11
MyArray(1, 2) = 12
MyArray(1, 3) = 13
MyArray(2, 0) = 20
MyArray(2, 1) = 21
MyArray(2, 2) = 22
MyArray(2, 3) = 23
Debug.Print MyArray(UBound(MyArray), 3)
End Sub

The UBound function is to give you elements in a variant array, you're passing it a range. You could try this:
Sub mains()
Dim myRange As Range, myArray As Variant
Set myRange = Range("A1:B4")
myArray = myRange.Value
Debug.Print test(myArray)
End Sub
Function test(list) As Double
test = UBound(list)
End Function
EDIT With a two dimensional range (as mentioned by KazimierzJawor) the default ubound will be the vertical, as you wanted, if you want to specify, you can add the optional perameter UBound(list, 1), but with the data you specified this defults to 4 as you wished

Ubound is a function that works on arrays you are passing it a range
try this
dim i(3)
i(0) = 1
i(1) =2
i(2) = 3
msgbox ubound(i)

Related

function to use an array

I am trying to write a function that uses the upper and lower bound of an array and returns the number of elements. It is confusing because of the lack of experience I have with functions but nonetheless I have come up with this code below:
Function numelement(LBound(array1), UBound(array2)) as Integer
numelement = UBound(array1) - LBound(array2)
End Function
Is this code correct, I know its simple but thats what the purpose of this code is to be.
If you realy want to have a function (you can do it easily without it), then try the code below:
Function numelement(array1 As Variant) As Long
numelement = (UBound(array1) - LBound(array1)) + 1 '<-- since array iz zero based
End Function
Sub Code to test the Function:
Sub TestFunc()
Dim Arr As Variant
Arr = Array("Shai", "Rado", "Tel-Aviv")
MsgBox numelement(Arr)
End Sub
Note: instead of the function, you can use:
Dim array1 As Variant
array1 = Array("Shai", "Rado", "Tel-Aviv")
MsgBox "number of elements in array are " & UBound(array1 ) + 1
You don't need a function for this. Ubound +1 works nicely for the total number of elements, if you initalize array like this Array(). It is a built-it function in VBA, it works great:
Option Explicit
Sub TestMe()
Dim myArr As Variant
myArr = Array("A", "B", "C")
Debug.Print LBound(myArr)
Debug.Print UBound(myArr)
End Sub
Unless you decide to initialize an array like this:
Dim arr(3 To 4)
arr(3) = 1
arr(4) = 2
Then you should use the following:
debug.print UBound(myArr)-LBound(myArr)+1

User-defined function returns non-formattable dates Excel VBA

I created a UDF in Excel VBA that returns an multi-dimensional array of dates and doubles. The problem is that I cannot format the dates that are returned.
Here is a simplified example:
Function test(dates as Range)
Dim results
Dim i As Integer
ReDim results(1 To dates.Cells.Count)
For i = 1 To dates.Cells.Count
results(i) = dates.Cells(i).Value
Next
test = Application.WorksheetFunction.Transpose(results)
End Function
The transpose at the end is just for convenience to have an column output (I press Ctrl+Shift+enter). I you use this simplified example, you will not be able to format the output and it will not be considered as dates stricto sensu.
Any ideas?
Change the results array to doubles:
Function test(dates As Range)
Dim results() As Double
Dim i As Integer
ReDim results(1 To dates.Cells.Count)
For i = 1 To dates.Cells.Count
results(i) = dates.Cells(i).Value
Next
test = Application.WorksheetFunction.Transpose(results)
End Function
Or change the dates.Cells(i).Value to dates.Cells(i).Value2 which will return the double not the date string:
Function test(dates As Range)
Dim results
Dim i As Integer
ReDim results(1 To dates.Cells.Count)
For i = 1 To dates.Cells.Count
results(i) = dates.Cells(i).Value2
Next
test = Application.WorksheetFunction.Transpose(results)
End Function
Then format the cells as you desire.
You may try something like this.
Results(i) = CDate(dates.Cells(i).Value)

Function will not return array when range contains only one value

I have a function meant to return an array which is created out of a single-column list of data. I have been using this function's return value essentially as a pseudo-global variable (LINENAMES_ARRAY) which I pass to many functions. Those functions than do checks on it such as If Len(Join(LINENAMES_ARRAY)) = 0 Then or go through items with For Each statements. Here is the code:
Function LINENAMES_ARRAY() As Variant
'returns an array of all items in the main sheet linenames column
LINENAMES_ARRAY = Application.Transpose(MAIN.Range( _
MAIN.Cells(MAIN_HEAD_COUNT + 1, MAIN_LINENAMES_COLUMN), _
MAIN.Cells(LINENAMES_COUNT + 1, MAIN_LINENAMES_COLUMN)))
End Function
I recently stumbled on one of those you-don't-see-it-till-you-see-it problems while using this workbook for a new project, where if the array happens to be only 1 element, everything fails. Apparently in that case, this returns a single value so Join() will fail For Each __ in LINENAMES_ARRAY will too. Why won't it treat this as a 1x1 array rather than a free value? I have started to mitigate the problem by rewriting functions where this is called, to check whether it is an array, then do some other procedure. Things like:
For j = 1 To LINENAMES_COUNT
LINES_BOX.AddItem lineNames(j)
Next j
is changed to:
If Not IsArray(LINENAMES_ARRAY) Then
myListBox.AddItem CStr(LINENAMES_ARRAY)
Else
For j = 1 To LINENAMES_COUNT
LINES_BOX.AddItem LINENAMES_ARRAY(j)
Next j
End If
However this becomes messy and is adding many extra checks to my code that I would prefer to handle in the LINENAMES_ARRAY function. Is there a way to return a 1x1 array? Or any other workaround?
An array can have a single element if you create it as a single element array and populate it in an array manner.
Option Explicit
Dim MAIN_HEAD_COUNT As Long
Dim LINENAMES_COUNT As Long
Dim MAIN_LINENAMES_COLUMN As Long
Dim MAIN As Worksheet
Sub stuff()
Dim arr As Variant
Set MAIN = Worksheets("Sheet1")
MAIN_LINENAMES_COLUMN = 2
MAIN_HEAD_COUNT = 2
LINENAMES_COUNT = 2
arr = LINENAMES_ARRAY()
Debug.Print IsArray(arr)
Debug.Print LBound(arr) & ":" & UBound(arr)
End Sub
Function LINENAMES_ARRAY() As Variant
Dim a As Long, tmp() As Variant
ReDim tmp(0 To LINENAMES_COUNT - MAIN_HEAD_COUNT)
For a = 0 To LINENAMES_COUNT - MAIN_HEAD_COUNT
tmp(a) = MAIN.Range(MAIN.Cells(MAIN_HEAD_COUNT + 1, MAIN_LINENAMES_COLUMN), _
MAIN.Cells(LINENAMES_COUNT + 1, MAIN_LINENAMES_COLUMN)).Cells(a).Value2
Next a
'returns an array of all items in the main sheet linenames column
LINENAMES_ARRAY = tmp
End Function
Results from the VBE's Immediate window:
True
0:0

#VALUE ERROR from my VBA function

So I wrote a simple function in VBA and I want to use it in my excel workbook. I wrote the following code:
Option Explicit
Public Function KOLICINA(fiksnacena As Long, ceni() As Long, nedela() As Long) As Long
Dim brojac As Integer
For brojac = 1 To UBound(nedela)
If Not ((IsEmpty(nedela(brojac) Or nedela(brojac) = 0) And ceni(brojac) <> fiksnacena)) Then KOLICINA = nedela(brojac)
Next brojac
End Function
When I try to use it in a worksheet cell (using =KOLICINA(18;G22:G26;H22:H26))
, I get the #VALUE error.
I don't understand why. The function should go through nedela Array and if it finds a Non empty or different value than 0 AND if the matching cell in the ceni Array is different from the number fiksnacena, it should return the value of the cell in nedela.
You cannot simply pass a cell range reference into a UDF and have it interpreted as a single dimensioned array of longs.
Public Function KOLICINA(fiksnacena As Long, ceni As Range, nedela As Range) As Long
Dim brojac As Long, vCeni As Variant, vNedela As Variant
vCeni = ceni.Value2
vNedela = nedela.Value2
For brojac = LBound(vNedela, 1) To UBound(vNedela, 1)
If Not ((IsEmpty(vNedela(brojac, 1) Or vNedela(brojac, 1) = 0) And vCeni(brojac, 1) <> fiksnacena)) Then
KOLICINA = vNedela(brojac, 1)
Exit For
End If
Next brojac
End Function
When you dump values from a range reference into an array, you always end up with a two dimensioned array; in your example it is 1 to 5, 1 to 1.
To further illustrate this point, your original UDF code would work if you pulled the values from the ranges after transposing them and finish off the UDF with CSE so that the values are processed as an array.
=KOLICINA(18, VALUE(TRANSPOSE(G22:G26)), VALUE(TRANSPOSE(H22:H26)))
Finalize with [ctrl]+[shift]+[enter].

Type Mismatch error when summing along one dimension in multi dimension array

I have a 2D array and I am trying to add along one dimension. The 2D Array is of type variant and might have some of the elements as null ("")
Here is the code so far
'newArray is 2D Array
Function SumColumn(newArray As Variant, index As Integer) As Double
Dim tempArray() As Double
ReDim tempArray(1 To UBound(newArray))
For i = 1 To (UBound(newArray))
tempArray(i) = CDbl(newArray(i, index))
Next
SumColumn = Application.WorksheetFunction.Sum(tempArray)
End Function
I get a type mismatch error when I am running the above code. Please help me debug
You are probabaly getting a Type mismatch because CDbl(newArray(i, index)) might actually not be a number.
This works for me. Please amend the code to suit your needs.
For demonstration purpose, I am storing an Excel range into a 2D array and then converting it to a 1D temp array. Once that is done, I am simply storing the relevant Numbers in the Double Array and finally calculating the sum.
Lets say that the worksheet looks like this
Option Explicit
Sub Sample()
Dim MyAr, TempAr()
Dim dAr() As Double
Dim n As Long, i As Long
MyAr = ActiveSheet.Range("A1:A10").Value
TempAr = Application.Transpose(MyAr)
ReDim dAr(0 To 0)
n = 0
For i = LBound(TempAr) To UBound(TempAr)
If Len(Trim(TempAr(i))) <> 0 Then
If IsNumeric(Trim(TempAr(i))) Then
ReDim Preserve dAr(0 To n)
dAr(UBound(dAr)) = Trim(TempAr(i))
n = n + 1
End If
End If
Next i
Debug.Print Application.WorksheetFunction.Sum(dAr)
End Sub
And this is the output