web-dev-qa-db-fra.com

Créer un dictionnaire de listes en vba

J'ai déjà travaillé dans Python auparavant, où il est très facile d'avoir un dictionnaire de listes (c'est-à-dire qu'une clé correspond à une liste de choses). Je me bats pour atteindre le même objectif dans vba. Disons que j'ai les données suivantes dans une feuille Excel:

Flanged_connections 6
Flanged_connections 8
Flanged_connections 10
Instrument  Pressure
Instrument  Temperature
Instrument  Bridle
Instrument  Others
Piping  1
Piping  2
Piping  3

Maintenant, je veux lire les données et les stocker dans un dictionnaire où les clés sont Flanged_connections, Instrument et Piping et les valeurs sont celles correspondantes dans la deuxième colonne. Je veux que les données ressemblent à ceci:

'key' 'values':

'Flanged_connections' '[6 8 10]'
'Instrument' '["Pressure" "Temperature" "Bridle" "Others"]'
'Piping' '[1 2 3]'

et pouvoir ensuite obtenir la liste en faisant dict.Item("Piping") avec la liste [1 2 3] comme résultat. Alors j'ai commencé à penser à faire quelque chose comme:

For Each row In inputRange.Rows

    If Not equipmentDictionary.Exists(row.Cells(equipmentCol).Text) Then
        equipmentDictionary.Add row.Cells(equipmentCol).Text, <INSERT NEW LIST>
    Else
        equipmentDictionary.Add row.Cells(equipmentCol).Text, <ADD TO EXISTING LIST>
    End If

Next

Cela semble un peu fastidieux à faire. Y a-t-il une meilleure approche à cela? J'ai essayé de rechercher l'utilisation de tableaux dans vba et cela semble un peu différent de Java, c ++ et python, avec stuft comme redim preserve et les «j'aime». Est-ce la seule façon de travailler avec des tableaux dans vba?

Ma solution:

Sur la base du commentaire de @varocarbas, j'ai créé un dictionnaire de collections. C’est le moyen le plus simple pour mon esprit de comprendre ce qui se passe, même si ce n’est peut-être pas le plus efficace. Les autres solutions fonctionneraient probablement aussi (non testé par moi). Ceci est ma solution suggérée et elle fournit la sortie correcte:

'/--------------------------------------\'
'| Sets up the dictionary for equipment |'
'\--------------------------------------/'

inputRowMin = 1
inputRowMax = 173
inputColMin = 1
inputColMax = 2
equipmentCol = 1
dimensionCol = 2

Set equipmentDictionary = CreateObject("Scripting.Dictionary")
Set inputSheet = Application.Sheets(inputSheetName)
Set inputRange = Range(Cells(inputRowMin, inputColMin), Cells(inputRowMax, inputColMax))
Set equipmentCollection = New Collection

For i = 1 To inputRange.Height
    thisEquipment = inputRange(i, equipmentCol).Text
    nextEquipment = inputRange(i + 1, equipmentCol).Text
    thisDimension = inputRange(i, dimensionCol).Text

    'The Strings are equal - add thisEquipment to collection and continue
    If (StrComp(thisEquipment, nextEquipment, vbTextCompare) = 0) Then
        equipmentCollection.Add thisDimension
    'The Strings are not equal - add thisEquipment to collection and the collection to the dictionary
    Else
        equipmentCollection.Add thisDimension
        equipmentDictionary.Add thisEquipment, equipmentCollection
        Set equipmentCollection = New Collection
    End If

Next

'Check input
Dim tmpCollection As Collection
For Each key In equipmentDictionary.Keys

    Debug.Print "--------------" & key & "---------------"
    Set tmpCollection = equipmentDictionary.Item(key)
    For i = 1 To tmpCollection.Count
        Debug.Print tmpCollection.Item(i)
    Next

Next

Notez que cette solution suppose que tous les équipements sont triés!

12
Krøllebølle

Les tableaux en VBA sont plus ou moins comme partout ailleurs avec diverses particularités:

  • Redimensionner un tableau est possible (bien que non requis).
  • La plupart des propriétés du tableau (par exemple, le tableau Sheets dans un classeur) sont basées sur 1. Bien que, comme l'a justement souligné @TimWilliams, les tableaux définis par l'utilisateur sont en réalité basés sur 0. Le tableau ci-dessous définit un tableau de chaînes d'une longueur de 11 (10 indique la position la plus haute).

À part cela et les particularités concernant les notations, vous ne devriez trouver aucun problème pour traiter les tableaux VBA.

Dim stringArray(10) As String
stringArray(1) = "first val"
stringArray(2) = "second val"
'etc.

En ce qui concerne ce que vous demandez, vous pouvez créer un dictionnaire dans VBA et y inclure une liste (ou l’équivalent de VBA: Collection). Voici un exemple de code:

Set dict = CreateObject("Scripting.Dictionary")
Set coll = New Collection
coll.Add ("coll1")
coll.Add ("coll2")
coll.Add ("coll3")
If Not dict.Exists("dict1") Then
    dict.Add "dict1", coll
End If

Dim curVal As String: curVal = dict("dict1")(3) '-> "coll3"

Set dict = Nothing 
8
varocarbas

Vous pouvez avoir des dictionnaires au sein de dictionnaires. Pas besoin d'utiliser des tableaux ou des collections sauf si vous en avez un besoin spécifique.

Sub FillNestedDictionairies()

    Dim dcParent As Scripting.Dictionary
    Dim dcChild As Scripting.Dictionary
    Dim rCell As Range
    Dim vaSplit As Variant
    Dim vParentKey As Variant, vChildKey As Variant

    Set dcParent = New Scripting.Dictionary

    'Don't use currentregion if you have adjacent data
    For Each rCell In Sheet2.Range("A1").CurrentRegion.Cells
        'assume the text is separated by a space
        vaSplit = Split(rCell.Value, Space(1))

        'If it's already there, set the child to what's there
        If dcParent.Exists(vaSplit(0)) Then
            Set dcChild = dcParent.Item(vaSplit(0))
        Else 'create a new child
            Set dcChild = New Scripting.Dictionary
            dcParent.Add vaSplit(0), dcChild
        End If
        'Assumes unique post-space data - text for Exists if that's not the case
        dcChild.Add CStr(vaSplit(1)), vaSplit(1)
    Next rCell

    'Output to prove it works
    For Each vParentKey In dcParent.Keys
        For Each vChildKey In dcParent.Item(vParentKey).Keys
            Debug.Print vParentKey, vChildKey
        Next vChildKey
    Next vParentKey

End Sub
4
Dick Kusleika

Je ne connais pas très bien C++ et Python (cela fait longtemps), donc je ne peux pas vraiment parler des différences entre VBA, mais je peux dire que travailler avec Arrays dans VBA n'est pas particulièrement compliqué. 

À mon humble avis, la meilleure façon de travailler avec des tableaux dynamiques dans VBA est de le dimensionner en un grand nombre et de le réduire lorsque vous avez fini d'y ajouter des éléments. En effet, Redim Preserve, où vous redimensionnez le tableau tout en sauvegardant les valeurs, a un coût en performances énorme. Vous ne devez JAMAIS utiliser Redim Preserve dans une boucle, l'exécution serait extrêmement lente

Adaptez le code suivant, donné à titre d'exemple:

Sub CreateArrays()

Dim wS As Worksheet
Set wS = ActiveSheet

Dim Flanged_connections()
ReDim Flanged_connections(WorksheetFunction.CountIf(wS.Columns(1), _
    "Flanged_connections"))

For i = 1 To wS.Cells(1, 1).CurrentRegion.Rows.Count Step 1

    If UCase(wS.Cells(i, 1).Value) = "FLANGED_CONNECTIONS" Then   ' UCASE = Capitalize everything

        Flanged_connections(c1) = wS.Cells(i, 2).Value

    End If

Next i

End Sub
1
Julien Marrec