Andreas Hild
Andreas Hild

Reputation: 79

Add three variables to scripting dictionary problem VBA

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

Answers (2)

L8n
L8n

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

Vityata
Vityata

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):

enter image description here

The latter has the advantage, that it is a bit faster and pressing Ctrl+space one would see the Intelli-Sense:

enter image description here

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

Related Questions