Iterating over a collection (VBA) - vba

Hello I am new to VBA and I am trying to iterate over a collection i've made and execute an action for each value.
Here is my code:
Sub makeRowsMatch()
Dim rows As VBA.Collection
Set rows = New VBA.Collection
Dim i As Integer
Dim j As Integer
Dim y As Integer
For i = 2 To 22203
For j = 2 To 121
If Cells(i, 2).Value <> Cells(j, 38) Then
rows.Add (i)
End If
Next j
Next i
For Each y As Object in rows
rows(y).Delete
Next y
End Sub
I keep on getting an error in the line:
For Each y As Object in rows
It keeps on getting highlighted yellow, but when I remove the above line is get the following error:
For Each control variable must be Variant or Object
Could someone explain why it is not letting me iterate over the values?
The whole issue of declaring variables is new to me.
Thanks!

Declare y as Variant, i.e.:
Dim y As Variant
and change your loop like this:
For Each y In rows
Then the loop will work or, at least, it won't give you an error at that point.
But the next problem/error is inside the loop. This line:
rows(y).Delete
will give you an error "Object required", or something like that, because rows(y) return an Integer, so you can't call Delete on it. There's some confusion here because rows on its own means the Rows range of the active sheet. But you also named your collection as rows, so there's a naming conflict.
Rename your rows to, for example, myrows and it should then all work. However, I don't know what you're trying to do with the Delete line, so I can't advise further.

You've got a conflict in your declarations and use. This line declares y as an Integer, clearly:
Dim y As Integer
But you then try to use it as an Object, either with
For Each y As Object in rows
or
For Each y in rows
Change the declaration to
Dim y As Object
(I'm not that familiar with VBA. If the above doesn't work, change the declaration to Dim y As Variant instead.)

Related

Dim n variables where n is a user defined number

I have to dim n variables in my software, where n is a number that user types in the interface. I'd start with a for loop, in order to declare these variables, with something like:
for i = 0 to n
dim r.i as IRow = worksheet.CreateRow(i)
next
This is what I think but obviously I know that's wrong. Does anyone knows if this is possible? I can't create neither List(Of) nor arrays because these are rows of an excel file I'm trying to export, otherwise NPOI returns me error.
What does Excel have anything to do with not using a List? You can absolutely use worksheet.CreateRow() with a list:
Dim rows As New List(Of IRow)
For i As Integer = 0 to n - 1
rows.Add(worksheet.CreateRow(i))
Next
Now the name of each row is row(0), row(1), row(2) ... row(n - 1)
If you've tried this and NPOI is giving you an error, you should show that code around where the error is thrown and tell us what the error is.
Based on comments:
'Assuming everything has the same number of entries as ListBox1, per the comments
Dim rows As New List(Of IRow)
Dim row As IRow = worksheet.CreateRow(0)
row.CreateCell(0).SetCellValue("Time")
row.CreateCell(1).SetCellValue("hrr")
For i As Integer = 0 To ListBox1.Items.Count - 1
row = worksheet.CreateRow(i+1)
row.CreateCell(0).SetCellValue(ListBox1.Items(i))
row.CreateCell(1).SetCellValue(ListBox2.Items(i))
rows.Add(row)
Next i

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

VBA Range change selection, value doesn't change

I have a range variable (called Constr) that is based on data that looks like this
Type Bound1 Bound2 Var1 Var2
X 1 2 3 4
Y 1 2 3 4
--
Z 1 2 3 4
I now use this procedure to change the selection to only the entries before the '--'
Sub Adjust_Selection(which As String, what_in As String, columns As Integer)
Dim row_nr_start As Integer
Dim row_nr_end As Integer
Dim row_nr_delta As Integer
Sheets("Main").Select
row_nr_start = Range(which).Find(what:=what_in, LookIn:=xlValues, LookAt:=xlWhole).Row
row_nr_end = Range(which).Find(what:="--", LookIn:=xlValues, LookAt:=xlWhole).Row
row_nr_delta = row_nr_end - row_nr_start
Range(which).Resize(row_nr_delta, columns).Select
This works and I can see that the selection changes, if I now call it using
Call Adjust_Selection("Constr", "Type", 5)
myitem("Constraints") = Range("Constr").Value
myitem is of type
Dim myitem As New Scripting.Dictionary
however when I access the value it still has everything in it. How can I update the value to only the first few lines up until the '--'?
You are calling Adjust_Selection with the named range Constr and afterwards refer to the named range Constraints. So, of course the result is different because you are referring to two different named ranges.
Furthermore, the named range Constr is not altered. It is merely used as a starting point and then a sub-set is Selected. But by selecting something you are not changing a named range (especially not a differently named range).
So, I am guessing that this is what you are searching for:
Call Adjust_Selection("Constr", "Type", 5)
ThisWorkbook.Names.Add Name:="Constraints", RefersTo:=Selection
myitem("Constraints") = Range("Constraints").Value
Note, that the selection of Adjust_Selection is now "saved" in the new named range Constraints and then myitem is being assigned this named range which is limited to the (correct) selection. Hence, the resulting variable (being a dictionary) contains all elements without the --.
Hi ThatQuantDude, I don't quite understand your question even after trying it out on my own. Based on the examples you gave, I assumed you want to store the selected range data into "Constraints" key? Apart from this, your sub function for selecting the range is working fine.
Call Adjust_Selection("Constr", "Type", 5)
myitem("Constraints") = Range("Constraints").Value
Appreciate if you could elaborate it further so I can better understand what you are trying to do? Thanks.
Range.Resize is a function. It will not change the range; it returns a new one. You just happen to select it, which isn't necessary. Turn your sub into a function returning the result of Range.Resize, and use this function directly on the right hand side of your assignment.
Note that you're not using the same name for your range in both lines of code, which I assume is a typo.

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.

excel vba moving non-contiguous range selection to an array

In the situation where the user select two non-contiguous column ranges i wrote the following:
Dim count long
Dim points variant
Dim i long
Set user_range = ActiveWindow.RangeSelection
count = user_range.count / 2
ReDim points(1 To count, 1 To 2)
For i = 1 To count
MsgBox "value is" & user_range.Areas.Item(1).Value(i,1)
points(i, 1) = user_range.Areas.Item(1).Value(i,1)
points(i, 2) = user_range.Areas.Item(2).Value(i,1)
Next i
But i get an object error when i try this. Am i indexing Value wrong?
This should work right? Is there an easier way to do this?
Any help is greatly appreciated!
Thanks,
Russ
I'm afraid your code does not compile. First of all, you need to declare your variables correctly. You should also use Option Explicit.
Option Explicit
Dim count As Long
Dim points As Variant
Dim i As Long
Dim user_range As Range
The count and ReDim lines are OK, but you are assuming that the two selections are both the same size. Will that always be the case?
Then I'm not sure what it is you want to do, but I'm guessing you just want to save the values in user_range into points.
You need to adress them a bit different:
points(i, 1) = user_range.Areas(1).Cells(i, 1).Value 'Selection 1
points(i, 2) = user_range.Areas(2).Cells(i, 1).Value 'Selection 2