Object required error '424' when iterating over Collection - vba

When I run my function, I get the following error:
Run-Time error '424': Object Required
And it highlights the following line of code:
For Each n In DataSet
Here is my full code:
'==============================|Find_Occurrences FUNCTION|=============================='
' Given a collection and a search value, return the number of occurrences of the search
' value in the collection, as an Integer. If it does not exist, return the integer 0
Function Find_Occurrences(DataSet As Collection, What As Variant) As Integer
Dim Count As Integer
Count = 0
Dim n As Variant
For Each n In DataSet ''''''''''''''''''Object required error'''''''''''''''''
'MsgBox (TypeName(What))
If (TypeName(n) = TypeName(What)) And (n = What) Then
Count = Count + 1
End If
Next n
Find_Occurrences = Count
End Function
Does anybody know what could be causing this, whether in my code or outside of my code?

The error was actually outside of the function, when the function is called. Instead of passing in a collection for DataSet, it passes in nothing.

Related

SSRSS trying to see if the results of a LookUpSet are in a string

I'm trying to list the results of a LookUpSet only if they are in another string.
Initially I used the following to get an array of strings seperated by a semi-colon.
Join(LookUpSet(...),";")
The next stage was to test if the strings were in the main string. I tried nesting the return value inside an IIF statement;
Join(LookUpSet(...,IIF(InStr(Fields!BigText.Value,Fields!Text.Value) > 0, Fields!Text.Value, Nothing),...),";")
I understand this doesn't work because the big string that I am comparing against is in my first dataset and the other is in my second dataset
Next I tried to use InStr on the results of the LookUpSet;
IIF(InStr(Fields!BigText.Value, LookUpSet()) > 0, LookUpSet(), Nothing)
This won't work either so I've tried to create custom code and am nearly there;
Code.CheckSetInString(Fields!BigText.Value, LookUpSet())
The code is below, when debugging I receive "Object reference not set to an instance of an object", but can't see anything that I've failed to declare.
How should I troubleshoot which object isn't declared?
The custom code is below for reference.
Function CheckSetInString(ByVal str As String, ByVal setofstrings As Object())
If setofstrings Is Nothing OrElse Not setofstrings.length > 0 Then
Return Nothing
End If
Dim returnedstrings AS [String]()
Dim i As Integer = 0
For Each item As String In setofstrings
If InStr(1, str, item, 1) > 0 Then
returnedstrings(i) = item
i += 1
End If
Next
Return returnedstrings
End Function
Any advice is appreciated,
Dan

Class Get Function Error ByRef

I have a custom class called a cCavity. One of the many properties of the class is a string array called pAdjacency, which contains the string name of a node at each index (Format is C[# of Node]). I am trying to create edge names by using two node names in conjunction with one another. Whenever I try to call the adjacency GET function for the class objects, I get a ByRef argument type mismatch, and I cannot see why.
Class Get function:
Public Property Get Adjacency(Index As Integer) As String
Adjacency = pAdjacency(Index)
End Property
Section of Code that is getting an error:
Sub CalculateEdges(cCavities() As cCavity, dEdges As Scripting.Dictionary)
'Dim i as integer
For i = 1 To UBound(cCavities)
If cCavities(i).AdjacencySize > MaxEdges Then MaxEdges = cCavities(i).AdjacencySize
'Dim j as Integer
For j = 1 To cCavities(i).AdjacencySize
dEdges.Add cCavities(i).Name & cCavities(i).Adjacency(j), 0 ' The error is taking place here, with the .Adjacency(j)
Next j
Next i
End Sub
The error in question is: "Compile error: ByRef argument type mismatch"
Am I missing something obvious? The argument j should be an integer, and I have tried explicitly defining it as such while trying to figure out what was happening.
First specify Option Explicit and declare all your variables, then make your parameters ByVal where it makes sense: object pointers and values can be passed by value, arrays must be passed ByRef.
So start with this:
Public Property Get Adjacency(ByVal Index As Long) As String
Adjacency = pAdjacency(Index)
End Property
Now, that 4-liner snippet is dereferencing cCavities(i) 5 times. Have you considered introducing a local variable, and dereferencing the object once?
For i = LBound(cCavities) To UBound(cCavities)
Dim foo As cCavity
Set foo = cCavities(i)
For j = 1 To foo.AdjacencySize
Dim edge As String
edge = foo.Name & foo.Adjacency(j)
Next
Next
Now, does that foo assignment work? If so, congrats, you now have IntelliSense on the cCavity member calls, and more efficient code.
It's a compile error, not a run-time error. If j is a Variant (as it will be if it's not explicitly declared), the compiler won't let it through. Declare all of your variables with their proper types.
Edit: for some reason, the compiler only complains about this at run-time, but it's still a compile error.

UnNest indefinite number of nested objects in vba

I would like to take any number of objects via a ParamArray and then add them, or variables nested within them to a collection. The tricky part is that if that nested object is a container of some sort (collection, scripting dictionary or even a custom class with a count method) also has variables nested within it, I want it to return those in the collection, NOT the container.
It would go something like this, let's start by creating a use case:
Sub MakeItems()
Dim ReturnedColl as Collection
Dim aString as String
Dim TopColl as New Collection, NestedColl as New Collection, SubNestedDic as New Dictionary
Dim aRangeofManyCells as Range, aRangeofOneCell as Range
Dim anObject as newObject, NestedObject as New Object, SubNestedObject as New Object
aString = "Just a string"
Set aRangeofManyCells = Range("A1:C3")
Set aRangeofOneCell = Range("A4")
SubNestedDic.Add SubNestedObject
SubNestedDic.Add aRangeofOneCell
NestedColl.Add SubNestedDic
NestedColl.Add NestedObject
NestedColl.Add SubNestedDic
NestedColl.Add aRangeofManyCells
TopColl.Add aString
TopColl.AddNestedColl
Set ReturnedColl = UnNest(TopColl, TopColl, anObject, Range("Sheet1:Sheet3!Q1"))
For each Item in ReturnedColl
'do something
Next Item
End Sub
Here comes the part I can't figure out.
I would want to do a loop like this making the Item the new Items, and then look into each Item within item (if it has any), but without losing track of the original Items, because I'll have to go to the next Item.
Function UnNest(ParamArray Items() as Variant) as Collection
For Each Item in Items
If Item 'is a container of some sort' Then
'some kind of loop through all nests, subnests, subsubnests,...
Else
UnNest.Add Item
Endif
Next Item
End Function
So the end result should be a collection that holds:
"Just a String" from aString
9 range objects corresponding to the cells Range("A1:C3") from aRangeofManyCells
1 range object corresponding to Range("A4"), from aRangeofOneCell
The objects anObject, NestedObject, and SubNestedObject
All of the above 2x, because I put TopColl as an argument to the Function 2x
And also,
an additional anObject, because I added that as an argument to the function
3 Range objects, corresponding to Sheet1Q1, Sheet2Q2, Sheet3Q3
I know that's a tall order, but there has got to be some way to do that loop.
Thanks for any help!
This routine would appear to solve one of your use cases. Certainly it worked for me although I was not passing anything other than regular variables and arrays.
One problem I could not overcome was that I could not determine the type of an Object. Unless you can solve that problem, I do not see how to achieve your entire objective.
Sub DeNestParamArray(RetnValue() As Variant, ParamArray Nested() As Variant)
' Coded Nov 2010
' Each time a ParamArray is passed to a sub-routine, it is nested in a one
' element Variant array. This routine finds the bottom level of the nesting and
' sets RetnValue to the values in the original parameter array so that other routine
' need not be concerned with this complication.
Dim NestedCrnt As Variant
Dim Inx As Integer
NestedCrnt = Nested
' Find bottom level of nesting
Do While True
If VarType(NestedCrnt) < vbArray Then
' Have found a non-array element so must have reached the bottom level
Debug.Assert False ' Should have exited loop at previous level
Exit Do
End If
If NumDim(NestedCrnt) = 1 Then
If LBound(NestedCrnt) = UBound(NestedCrnt) Then
' This is a one element array
If VarType(NestedCrnt(LBound(NestedCrnt))) < vbArray Then
' But it does not contain an array so the user only specified
' one value; a literal or a non-array variable
' This is a valid exit from this loop
Exit Do
End If
NestedCrnt = NestedCrnt(LBound(NestedCrnt))
Else
' This is a one-dimensional, non-nested array
' This is the usual exit from this loop
Exit Do
End If
Else
Debug.Assert False ' This is an array but not a one-dimensional array
Exit Do
End If
Loop
' Have found bottom level array. Save contents in Return array.
ReDim RetnValue(LBound(NestedCrnt) To UBound(NestedCrnt))
For Inx = LBound(NestedCrnt) To UBound(NestedCrnt)
If VarType(NestedCrnt(Inx)) = vbObject Then
Set RetnValue(Inx) = NestedCrnt(Inx)
Else
RetnValue(Inx) = NestedCrnt(Inx)
End If
Next
End Sub
Public Function NumDim(ParamArray TestArray() As Variant) As Integer
' Returns the number of dimensions of TestArray.
' If there is an official way of determining the number of dimensions, I cannot find it.
' This routine tests for dimension 1, 2, 3 and so on until it get a failure.
' By trapping that failure it can determine the last test that did not fail.
' Coded June 2010. Documentation added July 2010.
' * TestArray() is a ParamArray because it allows the passing of arrays of any type.
' * The array to be tested in not TestArray but TestArray(LBound(TestArray)).
' * The routine does not validate that TestArray(LBound(TestArray)) is an array. If
' it is not an array, the routine return 0.
' * The routine does not check for more than one parameter. If the call was
' NumDim(MyArray1, MyArray2), it would ignore MyArray2.
Dim TestDim As Integer
Dim TestResult As Integer
On Error GoTo Finish
TestDim = 1
Do While True
TestResult = LBound(TestArray(LBound(TestArray)), TestDim)
TestDim = TestDim + 1
Loop
Finish:
NumDim = TestDim - 1
End Function

VB.net For Each Loop with If Statement Error

When this code is ran, Visual Studio gives the error:
InvalidOperationException was unhandled
List that this enumerator is bound to has been modified. An enumerator can only be used if the list does not change.
Dim counter As Integer
For Each x In lstWinners.Items
If x = lstWinners.SelectedItem Then
counter += 1
End If
Next
Here's a screenshot:
http://i.cubeupload.com/lIoWDg.png
EDIT:
This can be fixed by adding this line at the beginning:
Dim anything as string = lstWinners.Text
But why does this error happen, and why does this fix it?
When you are going over the list with for each it kind of "locks" the array. A good way around this is to just copy the array for the iteration.
Just use Array.Copy(source, target, target.Length) where your source would be lstWinners.Items and target is an array you declare. Then do your for each loop on the array. Something like:
Dim counter As Integer
Dim tempcopy(lstWinners.Items.Count) As String
Array.Copy(lstWinners.Items, tempcopy, tempcopy.Length)
For Each x In tempcopy
If x = lstWinners.SelectedItem Then
counter += 1
End If
Next
Perhaps it is seeing the = operator as the assignment operator instead.
Try this code instead:
Dim counter As Integer
For Each x In lstWinners.Items
If x Is lstWinners.SelectedItem Then
counter += 1
End If
Next
Is explicitly compares equality on objects, so it removes any potential ambiguity.
Assuming that your items are strings and you want to count the items with the same text then you could use
Dim counter As Integer
Dim x = lstWinners.SelectedItem.ToString()
counter = lstWinners.Items.Cast(Of String).Count(Function(z) z = x)
However, your code should not give that error unless there is something else that is running and modify your list (a separate thread?)

How to dynamically reference an object property in VBA

I'm trying to write a VBA function that counts the objects in a collection based on the value of one of the object's properties. I need the examined object property to be dynamic, supplied by the function parameters. I could use an if then statement, but that would have many, many elseif clauses, each with identical procedures, except the property name.
I'd like to avoid repeating my code over and over for each property name. Here's what I have so far.
Private Function getTicketCount(c As Collection, f As String, s As String) _
As Long
' #param c: collection of Ticket objects.
' #param f: property to filter.
' #param s: filter string.
'
' Function returns number of tickets that pass the filter.
Dim x As Long
Dim t As Ticket
x = 0
For Each t In c
If t.f = s Then x = x + 1 ' Compiler throws "Method or data member not found."
Next t
getTicketCount = x
End Function
The issue I'm having is that the compiler is looking for the "f" property of t instead of the value-of-f property of t. The exact error is commented in the code block above. How do I use the value of f instead of "f" to reference the object property?
I believe you want to use the CallByName method CallByName MSDN Link
Private Function getTicketCount(c As Collection, f As String, s As String) _
As Long
' #param c: collection of Ticket objects.
' #param f: property to filter.
' #param s: filter string.
'
' Function returns number of tickets that pass the filter.
Dim x As Long
Dim t As Ticket
x = 0
For Each t In c
If CallByName(t, f, VbGet) = s Then x = x + 1 ' Compiler throws "Method or data member not found."
Next t
getTicketCount = x
End Function