vba: lbound error on dates array - vba

I have a "Datevarie" dynamic matrix which contains many dates.
At some point in the routine I have to extract from DateVarie the lowest value.
The problem is that when I use the Lbound function, I always return the value 1 instead of the date (for example "01/01/2018").
I think the data in the array is entered correctly as I debugged it step by step several times.
How can I resolve without having to report data on a sheet and order them? Do I have to use FOR ... NEXT? The dates can be 2-3 but also several tens
Type interventi
...
data as date
...
public Intervento as interventi
public rs1 as adodb.recordset
...
Sub TheSubWithProblem(...)
...
dim Datevarie() as date
..
redim preserve etc..
DateVarie(k)=format(RS1!DataI,"dd/mm/yyyy") '<<<< correct
...
Intervento.data=lbound(datevarie) '<<<< always return 1
...

The problem is that when I use the Lbound function, I always return the value 1 instead of the date (for example "01/01/2018").
LBOUND and UBOUND functions return the lower- and upper bounds of the array, not the lowest and highest values within the array.
Arrays in VBA are base-0 by default, so an initialized array should typically have LBound of 0.
If you want to get the lowest or highest value, then you need to implement your own algorithm to do so (e.g., a for/next loop), or if this is a single-dimensional array we can potentially make use of an ArrayList object which has (among other things) a Sort method:
Set d = CreateObject("System.Collections.ArrayList")
Dim dateVarie As Variant
Dim i As Long, m As Long, day As Long
For i = 1 To 100
m = Application.WorksheetFunction.RandBetween(1, 12)
day = Application.WorksheetFunction.RandBetween(1, 30)
d.Add (DateValue(DateSerial(2018, m, day)))
Next
'Preserve your un-sorted array, if needed:
ReDim dateVarie(d.Count - 1)
dateVarie = d.ToArray()
Msgbox "The first value is: " & dateVarie(0)
d.Sort ' Sorts the arraylist:
MsgBox "The lowest value is: " & (d(0))

Arrays start at 0. This is why the below returns 0:
Sub TestMe()
Dim datevarie(2) As Date
Debug.Print LBound(datevarie)
End Sub
And it does not matter, whether you put values in the array or not.
If you want an array to start at 1, in VBA write Option Base 1 and make sure that noone sees the code, because you may have problems with your colleagues/peers:
Option Explicit
Option Base 1
Sub TestMe()
Dim datevarie(2) As Date
Debug.Print LBound(datevarie)
End Sub

Related

Issue with VBA array Filter function: only first result is returned

I am running into some issues while using VBA its 'Filter' function. I am trying to use multiple subsequent filters to step by step reduce the array down the contain the value I need. It however seems that only the first element that matches the 'Match' value is returned and not the subsequent ones?
To given an example, when running the code below, the second 'debug call' return '1' but it should return '2'
Function FilterAnArray()
Dim names As Variant
names = Array("Ann Smith", "Barry Jones", "John Smith", "Stephen Brown", "Wilfred Cross")
Debug.Print UBound(names)
Dim smithNames As Variant
smithNames = Filter(names, "Smith")
Debug.Print UBound(smithNames)
End Function
Version information e.t.c.
I am running Excel 2016, version 16.0.8730.2046 - 64-bit.
Any help with this issue is much appreciated!
It is working quite ok. It is returning 1, because you have two Smith's:
And the Upper bound of smithNames is 1, because it arrays are 0-based. If you want the count of the array elements, and you do not like the UBound+1, you may use a worksheet function:
Debug.Print WorksheetFunction.CountA(smithNames)
Strongly not adviseable option:
You may consider writing Option Base 1 on the top of the module. Then the arrays will be 1-based and will be the way you expect it. In your example you see why Option Base 1 is not advisable. If you have it, Names will be 1 based and smithNames will be 0 based. This is because of the different way the arrays are assigned.
Ubound gives the uppfer bound. As you use 0-based arrays you get a result of 4 for 5 entries and a result of 1 for 2 entries.
If you want to use 1-based array without explicitly dimensionen each array use Option Base 1 at the beginning of you Module.
How about checking how many results you ought to get and then printing them, such as:
Sub foo()
Dim val As Variant
Dim names As Variant
Dim post As Variant
names = Array("Ann Smith", "Barry Jones", "John Smith", "Stephen Brown", "Wilfred Cross")
Debug.Print UBound(names)
Dim smithNames As Variant
smithNames = Filter(names, "Smith")
pos = Application.Match(smithNames, names, False) 'find how many values match "Smith"
If Not IsError(pos) Then
For x = LBound(pos) To UBound(pos)
Debug.Print pos(x)
Next x
Else
MsgBox val & " not found!"
End If
End Sub

Passing arrays to UDF; UBound

It's quite frustrating that I already seem to be doing something wrong in the first line of my first VBA project. In essence my project has already been stalled because I can't figure out why this works:
Function TestF(Values As Variant, Dates As Variant)
TestF = Values(2)
End Function
But this does:
Function TestF(Values As Variant, Dates As Variant)
TestF = UBound(Values)
End Function
(I just want to see that UBound works. So, I removed everything from the project that is not related.)
Where Values and Dates are each supposed to be a 1-dimensional array. (Or ranges? Or is that the issue?)
How do I fix the first bit of code?
I Googled and I Googled, but I haven't found my answer.
The (Excel) 'error' is get is #VALUE! from =testf(A2:A10,B2:B10), where the ranges are like:
-1000 31-Dec-13
-10 31-Dec-14
30 13-Mar-15
1200 17-Mar-15
-40 30-Jun-15
1300 30-Sep-16
1200 31-Oct-17
1250 30-Nov-18
1500 31-Dec-18
The problem in your passing of array to UDF is your understanding about array and ranges. In general, ranges are presented as a two dimensional array even when they are with single row or column.
To make your code work, try the "Double Transpose Trick" which is pretty much the following:
Public Sub TestMe()
Dim k As Variant
k = Range("A2:F2")
Debug.Print k(1)
With WorksheetFunction
k = .Transpose(.Transpose(k))
Debug.Print k(1)
End With
End Sub
The code above will hopefully result as an error in the Debug.Print k(1) part. This is because k is a two-dimensional array, although it is on one row only:
Comment this part or change it to Debug.Print k(1, 1) and continue with the .Transpose It will change your array to a single dimension and it will be working.
Thus, if you want your non-working formula to work, this is a possible way to do it:
Public Function TestFormula(myValues As Variant) As String
Dim myArr As Variant
With WorksheetFunction
myArr = .Transpose(.Transpose(myValues))
TestFormula = myArr(UBound(myArr))
End With
End Function
Some important notes: the "Double Transpose Trick" works on one row ranges, when these should be transfered to array. If you are having a one column range, use "Single Transpose":
myArr = .Transpose(myValues)
With more than 1 column or 1 row, do not use transpose as it cannot be mapped to a 1 dimensional array.
Furthermore, if you pass a range of once cell, it will not work. But you will find some way to go around it.
As mentioned by #cyboashu in the comments, the myArr() array would take values up to column 65536, because the Transpose() is made this way, probably for compatibility reasons with Excel 2003. To make a bigger array - Put entire column (each value in column) in an array?
You need to pass the right parameters to your UDF.
Both parameters are Range.
Function TestF(Rng1 As Range, DatesRng As Range) As Long
Dim C As Range
Dim x As Long
For Each C In Rng1
If Trim(C.Value) <> "" Then ' make sure there isn't a blank cell in the middle of your Range
x = x + 1
End If
Next C
TestF = x - 1
End Function
If you don't have empty cells in the middle of your Range, then the following code will be sufficient:
Function TestF(Rng1 As Range, DatesRng As Range) As Long
Dim C As Range
TestF = Rng1.Cells.Count - 1
End Function
You probably wanted:
TestF=Values(Ubound(Values))
also, you should declare what your function is suppose to return, like
Function TestF(Values As Variant, Dates As Variant) as String

Converting input range to array in function

I have been learning VBA and thought I was getting the hang of it, but for some reason this basic task is eluding me. I am trying to create a function where I select an input region of numbers (really only one row or column), and then output the summation of the numbers in a cell. Here is my code:
Function CashFlow(CashArray As Excel.Range)
Dim cashflows() As Variant
cashflows = CashArray.Value
amount = CashArray.Rows.Count
dim y()
redim y(amount)
Sum = 0
For i = 1 To amount
y(i) = cashflows(i)
Sum = Sum + y(i)
Next i
CashFlow = Sum
End Function
Despite me knowing how to do essentially this in a subroutine, the fact that I'm getting my data from the function's input is throwing me off. How do I accomplish this task?
Just to summarize the above comments, please give the following code a try:
Option Explicit
Function CashFlow(CashArray As Excel.Range)
Dim sum As Double
Dim x As Long, y As Long
Dim cashflows() As Variant
cashflows = CashArray.Value
sum = 0
For y = LBound(cashflows, 1) To UBound(cashflows, 1)
For x = LBound(cashflows, 2) To UBound(cashflows, 2)
sum = sum + cashflows(y, x)
Next x
Next y
CashFlow = sum
End Function
Note, that this code summarizes all cells in the given range (even if there are multiple rows and columns in that range). Basically, the range is stored in the array cashflows. As mentioned in the comments, this will yield a two-dimensional array. The function LBound(cashflows) will give you the Lower Boundary of that array.
By specifying LBound(cashflows, 1) I am specifically asking for the lower boundary of the first dimension (in this case the rows). UBound returns the Upper Boundary of that array for the specified dimension. Similarily UBound(cashflows, 2) returns the last column (second dimension) of the given array.
Remark: I am always using LBound() and UBound() in my code instead of starting with 0 or 1 to avoid coding errors. Some people prefer Option Base 0 and some prefer Option Base 1. While neither will have an impact on the above (as ranges always yield an array starting with 1) it is just good coding practice to prevent bugs in the future.

Remove duplicate rows based on dynamic amount of columns

I got the total number of columns, and tried to append it to string in such a way it is 1,2,3,4,5 depending on the number of columns.
Sub DeleteHighlightedRecords()
numofCol = ""
For i = 1 To lcol
numofCol = numofCol & "," & i
Next i
numofCol = Mid(numofCol, 2)
ThisWorkbook.Worksheets(1).Cells.RemoveDuplicates Columns:=Array(numofCol), Header:=xlNo
End Sub
The above code i have will match the combination i placed in Array(), and then remove the duplicates ya? Correct me if i am wrong, however I am getting application-defined error.
How may i set the dynamic numbers of columns into the Array()?
Array(1,2,3) is not the same as Array("1,2,3"). The first will produce an 3-element array containing 1,2 and 3, the second will produce an 1-element array containing the string "1,2,3".
To create an actual array we can do the following:
Dim numOfCol() As Variant 'normally I would choose Long instead of variant but that gave a type mismatch error
Dim i As Long 'always use "Option Explicit" at the top of your module!
ReDim numOfCol(0 To (lcol - 1)) 'has to be 0-based for RemoveDuplicates
For i = 0 To (lcol - 1)
numOfCol(i) = i + 1
Next i
Now as if using a Variant array instead of a Long array and using a 0-based array wasn't enough, RemoveDuplicates apparently also can't handle arguments passed as a reference to a variable instead of a value. To pass a variable as a value rather than a reference, encase it in parentheses. This causes the variable to be evaluated and then passed to the function.
ThisWorkbook.Worksheets(1).Cells.RemoveDuplicates Columns:=(numOfCol), Header:=xlNo

Count characters between two empty space to dashes() in vba

How do I get the length of character between beginning with space and ending with * Here is the image. Column B shows the total len before dasher(-) and my code
Sub xn()
Dim x As Integer
x = 1
If Worksheet("Sheet1").Range("A"& x).len(Right," ") Or _
Worksheet("Sheet1").Range("A"&x)len(Left,"-") Then
len(totallen)
End If
x = x + 1
End Sub
The code posted has multiple issues:
Worksheet is not a valid object - you need to use Worksheets.
.len is not a property of a Range object.
Even in .len was a property of a Range, you would need a
de-reference operator (aka '.') in here: Range("A"&x)len(Left,"-")
If you intend to use the function Len(), it only takes one argument.
You apparently are trying to loop, but you need to use either a For
or For Each loop - it won't loop automatically when you increment x
at the bottom of the sub.
Right is a function, but you're calling it without arguments and they are not optional.
Similarly, Left is a function, but you're also calling it without
the required arguments.
totallen is not declared anywhere, so Len(totallen) will assume
that totallen is a Variant (default for undeclared variables), then
cast it to a String, and then always return 0 because it has never
been given a value.
Anything else I may have missed.
The solution is to use the InStr function. It returns the location in a string of a given sub-string.
Sub xn()
Dim x As Long
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Worksheets("Sheet1")
For x = 1 To sheet.Range("A" & sheet.Rows.Count).End(xlUp).Row
sheet.Cells(x, 2) = InStr(1, sheet.Cells(x, 1), "-") - 1
Next x
End Sub
I'd also recommend taking a look at the MSDN article on Looping Through a Range of Cells (2003 vintage, but still valid), and Error Finding Last Used cell In VBA.