4

My objective is to use an array of names to create dynamic variables in VBA, heres the code:

Sub mymacro()
    Dim names()
    names = Array("cat_code()", "dog_code()", "eagle_code()")
    For Each c In names
        Dim c As Integer
    Next c
End Sub

And of course my real name array has hundreds of animals so it would be rather boring doing Dim for each and every one of them. The error I'm getting is Compile Error: Duplicate declaration in current scope

What is the best feasible solution to my objective?

4
  • do you need the arrays to exist? What for? What are you trying to do? Commented Sep 24, 2013 at 10:53
  • Thank you for your answer mehow as always, and thank you too Mike Woodhouse, not quite what I was hoping for, but both very clever nevertheless I think. I´m going to update my question and see if that creates an answer that I´m happy with. Commented Sep 24, 2013 at 12:14
  • 7
    the problem as you stated originally should be solved now, if you have another question consider asking a new one, based on what you have learned in this question. Try to avoid changing your question in a way that would invalidate existing good answers Commented Sep 24, 2013 at 13:27
  • 2
    ok mehow, I see your point, and I will ask another question Commented Sep 24, 2013 at 14:17

3 Answers 3

6

The compile error you are getting is caused by a duplicate declaration in the current scope.

In other words: this means you are declaring more than one variable with the same name.

Adding an Option Explicit statement on top of you modules requires you to declare each variable you use. It's very helpful when you receive this error because you can quickly scan your code for duplicate declaration of the highlighted line Dim <variable_name>

This is a sample demonstrating why you are getting the error:

Option Explicit

Sub Main()

    Dim c As Worksheet
    For Each c In Sheets
        Dim c As Long   ' you are going to get an error in here because
                        ' a variable named: c, is already declared within the sub
                        ' you can't have two variables named: c.
        For c = 1 To ws.Range("A" & Rows.Count).End(xlUp).Row
            ' some code
        Next c
    Next

End Sub

There is no easy work around your problem. We would have been able to provide a better solution to your problem if you better explain what you are trying to achieve.

There is a workaround to achieve what you want but I wouldn't recommend doing it this way if you are unsure of you are actually doing ;). The below code will create a new module in your current VBA project. While iterating over the array with the animal names it will be writing new lines to Module2 so after the execution your module two will be

enter image description here

In order for this code to work you have to add references to Microsoft Visual Basic for Applications Extensibility 5.3". You can do that by selectingTools>>References` in the VBE window.

Also, this requires you to Trust Access to VBA Project Object Model. Go to Excel Settings >> Trust Centre >> Macros >> tick Trust Access To VBA Project Object Model.

enter image description here

Run the sample code.

Option Explicit

' this VBA project requires
' 1 - references to Microsoft Visual Basic For Applications Extensibility 5.3
'     add it via Tools > References
'
' 2 - trust access to VBA project object model
'     In spreadsheet view go to Excel(application options) >> Trust Centre >> Macro Settings
'     tick the Trust Access to VBA project object model

Sub mymacro()
    Dim names
    names = Array("cat_code", "dog_code", "eagle_code")
    Dim c As Variant
    AddAModule
    For Each c In names
        ' dynamically create arrays
        WriteToModule CStr(c)
    Next
    CloseModule
End Sub


Private Sub AddAModule()
    Dim VBProj As VBIDE.VBProject
    Dim VBComp As VBIDE.vbComponent
    Dim CodeMod As VBIDE.CodeModule

    Set VBProj = ThisWorkbook.VBProject
    Set VBComp = VBProj.VBComponents.Add(vbext_ct_StdModule)
    Set CodeMod = VBComp.CodeModule

    With CodeMod
        .DeleteLines 1, .CountOfLines
        .InsertLines 1, "Public Sub DynamicallyCreatedArrays()"
        .InsertLines 2, "    ' code for the sub"
    End With
End Sub

Private Sub WriteToModule(arrayName As String)
    With ActiveWorkbook.VBProject.VBComponents("Module2").CodeModule
        .InsertLines .CountOfLines + 2, "    Dim " & arrayName & " as Variant"
    End With
End Sub

Private Sub CloseModule()
    With ActiveWorkbook.VBProject.VBComponents("Module2").CodeModule
        .InsertLines .CountOfLines + 2, "End Sub"
    End With
End Sub
Sign up to request clarification or add additional context in comments.

Comments

6

VBA can't really do what you're trying to do without getting into a horrible world of complications.

How about using a VBA Collection object instead? You'll need to create a simple class to hold the number, because VBA collections work with references, not values.

So I created a Class and set its name to "AnimalCounter", with this content:

Public Counter As Integer

Then your macro becomes something like this:

Sub mymacro()

Dim coll As New Collection
Dim c As Variant
Dim ac As AnimalCounter

    For Each c In Array("cat", "dog", "eagle")
        Set ac = New AnimalCounter
        coll.Add ac, c
    Next

    Debug.Print coll("cat").Counter ' what's in "cat"?
    coll("dog").Counter = coll("dog").Counter + 1 ' update "dog" by one
    Debug.Print coll("dog").Counter ' "dog" should now be one more

End Sub

If you wanted arrays, put an array in to the class. Or another Collection, maybe?

1 Comment

+ 1 For suggesting Collection!
4

Mike Woodhouse has the right idea of using a Collection with the keys of the animals. I add two notes:

First, I would recommend using a Dictionary instead. It is faster than a Collection, and allows explicit access to the Keys and Items collections. With a Collection, there is actually no way to fetch the keys, since the basic purpose is an ordered list of items rather than a order-agnostic hash as with a Dictionary.

For early-bound use of the Dictionary type, add a reference to Microsoft Scripting Runtime.

Second, do not use an array for the individual animals!. The reason is because arrays in VBA use by-value semantics ( see Collections in VBA – Overview, Values and References in VBA, Array Assignment Rules for more information). In short, every time you fetch an instance of an array from the containing Collection or Dictionary, you will be getting a new copy of the entire array. Thus any changes you make to the content of that array will not affect the actual array in the Dictionary or Collection. To get around this, use a Collection instead. This will use by-reference semantics and makes it much easier to append new items.

So here's what you'd want to do:

Sub ReadCodes() 
    Dim ws As Worksheet
    Dim strAnimalName  As String
    Dim dctAnimalCodes As New Dictionary
    Dim colAnimalCodes As Collection
    Dim lngAnimalCode  As Long 
    
    Set ws = Worksheets("Animal Code Data")
    For iRow = 1 To ws.UsedRange.Rows.Count
        strAnimalName = ws.Cells(iRow, 1)
        lngAnimalCode = ws.Cells(iRow, 2)
        
        ' Easy to check if key exists
        If Not dctAnimalCodes.Exists(strAnimalName) Then
            Set dctAnimalCodes(strAnimalName) = New Collection
        End If
        
        ' Getting the collection for this animal
        Set colAnimalCodes = dctAnimalCodes(strAnimalName)
        
        ' Easy appending of new code
        colAnimalCodes.Add lngAnimalCode
    Next 
End Sub 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.