Reputation: 79
I'm quite new to VBA (2 months in) and I'm trying to add three variables to a scripting dictionary in order to reformat an Excel Table and I am running into an error.
I have tried to add three variables by
countrydict.Add country, data, time
But I get an error message
Run-time error '450':
Wrong number of arguments or invalid property assignment
However it works if I write
countrydict.Add country, data 'or
countrydict.Add country, time
Dim lastrow As Long
Dim iter As Long
Dim diter As Long
Dim countrydict As Object
Dim country As String
Dim data As String
Dim time As String
Dim key As Variant
Dim i As Long
Const StartRow As Byte = 2
lastrow = Range("A" & StartRow).End(xlDown).Row
Set countrydict = CreateObject("Scripting.Dictionary")
Dim diter2 As Long, arr, arr2
With ActiveSheet
For iter = 2 To lastrow
country = Trim(.Cells(iter, 1).Value) '<<<<<
data = Trim(.Cells(iter, 2).Value) '<<<<<
time = Trim(.Cells(iter, 3).Text) '<<<<<
If countrydict.Exists(country) Then
If Not InStr(1, countrydict(country), data) > 0 Then
countrydict(country) = countrydict(country) & _
"|" & data & "/" & time
End If
Else
countrydict.Add country, data, time '<<<<<<<
End If
Next
iter = 2
For Each key In countrydict
.Cells(iter, 1).Value = key & ":"
.Cells(iter, 1).Font.Bold = True
.Cells(iter, 1).Font.ColorIndex = 30
iter = iter + 1
arr = Split(countrydict(key), "|")
For diter = 0 To UBound(arr)
arr2 = Split(arr(diter), "/")
.Cells(iter, 1).Value = arr2(0)
.Cells(iter, 2).Value = arr2(1)
Next diter
Next key
End With
End Sub
The expected result is to reformat a table in this format
"A" "B" "C"
EU Sales 10:00
EU Tax 12:00
USA Sales 09:00
USA Tax 10:00
Into this format
EU:
Sales 10:00
Tax 12:00
USA:
Sales 09:00
Tax 10:00
Many thanks for any help. I've been struggeling with this problem for days...
Upvotes: 2
Views: 598
Reputation: 728
Another possibility is to create a new class to store your data. Store your data in an instance of this class and then pass this object to your dictionary.
This way you could event extend the class to return other stuff, for example all values as a combined string etc... Using public properties you can even set up input validation and what not, but this is probably more than what is needed right now.
I kept the "Class" to the absolute minimum, normally public variables in classes are bad, but since we only use it as custom datatype this does not matter.
Edit: I updatet the class a bit to show some more functionality, but I leave the old one here as an example.
Standard Module "Module1":
Option Explicit
Sub fillDict()
Dim adict As Scripting.Dictionary
Set adict = New Dictionary
Dim info As myRegionData
Dim iter As Long
For iter = 0 To 10
Set info = New myRegionData
info.Region = "someRegion" & iter
info.data = "someData" & iter
info.Time = "someTime" & iter
adict.Add info.Region, info
Next iter
Dim returnInfo As myRegionData
Set returnInfo = adict.Item("someRegion1")
With returnInfo
Debug.Print .Region, .data, .Time 'someRegion1 someData1 someTime1
Debug.Print .fullSentence 'At someTime1 I was in someRegion1 and did someData1
End With
End Sub
Class Module (simple) "myRegionData":
Option Explicit
Public Region As String
Public data As String
Public Time As String
Class Module (extended) "myRegionData":
Option Explicit
Private Type TmyRegionData
'More about this structure:
'https://rubberduckvba.wordpress.com/2018/04/25/private-this-as-tsomething/
Region As String
data As String
Time As String
End Type
Private this As TmyRegionData
Public Property Get Region() As String
Region = this.Region
End Property
Public Property Let Region(value As String)
this.Region = value
End Property
Public Property Get data() As String
data = this.data
End Property
Public Property Let data(value As String)
this.data = value
End Property
Public Property Get Time() As String
Time = this.Time
End Property
Public Property Let Time(value As String)
this.Time = value
End Property
Public Function getFullSentence() As String
getFullSentence = "At " & Time & " I was in " & Region & " and did " & data
End Function
Upvotes: 2
Reputation: 43585
VBA has a dictionary structure. Dictionary is an object, and it can be referenced either with early binding (likeSet countrydict = CreateObject("Scripting.Dictionary")
) or with a late binding, referring to Microsoft Scripting Runtime (In VBEditor>Extras>Libraries):
The latter has the advantage, that it is a bit faster and pressing Ctrl+space one would see the Intelli-Sense:
Concerning the question with multiple variables to a dictionary, then an array with those is a possibility:
Sub MyDictionary()
Dim myDict As New Scripting.Dictionary
If Not myDict.Exists("Slim") Then
Debug.Print "Adding Slim"
myDict.Add "Slim", Array("Eminem", "has", "a", "daughter!")
End If
If Not myDict.Exists("Barcelona") Then
Debug.Print "Adding Barcelona"
myDict.Add "Barcelona", Array("I", "have", "been there", 2018)
End If
If Not myDict.Exists("Barcelona") Then
myDict.Add "Barcelona", Array("I", "have", "been there", 2018)
Else
Debug.Print "Barcelona already exists!"
End If
'Keys
Dim key As Variant
For Each key In myDict.Keys
Debug.Print "--------------"
Debug.Print "Key -> "; key
Dim arrItem As Variant
For Each arrItem In myDict(key)
Debug.Print arrItem
Next
Next key
End Sub
This is the result of the code:
Adding Slim
Adding Barcelona
Barcelona already exists!
--------------
Key -> Slim
Eminem
has
a
daughter!
--------------
Key -> Barcelona
I
have
been there
2018
If the value of the dictionary is not an array, e.g. adding somewhere myDict.Add "notArray", 124
, an error would pop up once it tries to print the array. This can be avoided with the usage of IsArray built-in function.
Upvotes: 2