Microsoft 365 Faire une collection de collection en VBA

GuillaumeFranceschi

XLDnaute Nouveau
Bonjour à tous,

Je me présente, je suis nouveau membre sur Excel-Downloads, je m'appelle Guillaume et je suis très heureux de pouvoir lire tous les post intéressants que le forum propose.
Pour mon entrée en matière sur le forum j'aimerais vous partager une problématique pour laquelle je sillonne internet et ce forum depuis quelques jours en quête d'une solution... sans succès pour le moment. Je vous expose mon problème du mieux que je le peux mais n'hésitez pas à me faire préciser tout ce qui nécessite éclaircissement :

Je travaille actuellement dans une équipe informatique à la SNCF au sein de laquelle je suis amené à manipuler des données ferroviaires. Ces données ferroviaires ne sont rien d'autre qu'une liste de voyages avec des points de passage. La liste Excel se présente ainsi :

1690538073787.png


Là c'est un échantillon dans lequel un seul voyage est représenté mais il faut imaginer des tableaux avec des milliers de lignes. J'ai donc mes points de passage sur la colonne B et dans la colonne A j'ai le numéro du voyage (c'est l'équivalent du numéro de train que vous voyez en gare quand vous voyagez :)) qui est répété sur chaque ligne.
Voilà, seulement ce format de représentation des données ne me convient pas pour la suite et j'aimerais ranger ces données dans une collection de voyage qui s'appelle "tripCollection" dans le fichier que je vous ai joint.
Je suis bien loin de maitriser les collections mais faire une collection d'objets Voyages ne me semble pas insurmontable. Pour ceci j'ai :
  • Créé un module de classe qui se nomme "trip" et dans laquelle j'ai défini les variables de la manière suivante :
VB:
Option Explicit

Public numero As String
Public listePoints As Collection

  • Mon problème va se situer dans la définition d'une variable comme une collection mais nous allons voir ça plus bas
  • J'ai ensuite créé une procédure pour instancier mes objets "Voyages" et les insérer à ma collection "tripCollection" en fonction des informations que je trouve dans mon tableau :
VB:
Dim tripCollection As New Collection
Sub remplirTripCollection()

Dim i, derligne, k As Long
Dim numVoyage As String
Dim voyage As trip
Dim listePointsVoyage As New Collection

derligne = Sheets("test").Cells(Rows.Count, 1).End(xlUp).Row

For i = 1 To derligne

    numVoyage = Sheets("test").Cells(i, 1).Value
    
    Set voyage = New trip
    voyage.numero = numVoyage
    
    
    k = i
    Set listPointsVoyage = Nothing
    
    Do While Sheets("test").Cells(k, 1).Value = numVoyage
    
        nouveauPoint = Sheets("test").Cells(k, 2).Value
        listePointsVoyage.Add nouveauPoint
        
    k = k + 1
    Loop

voyage.listePoints = listePointsVoyage
tripCollection.Add voyage

Next i

End Sub

  • En fait je veux faire une collection de voyages, mais pour chaque voyage j'aimerais que la liste de points (qui est variable car différente pour chaque voyage) soit également stockée dans une "sous-collection". C'est pour cela que je me suis dit que mettre une collection "listePoints" en variable de ma classe "trip" était une bonne idée mais en fait ça ne ne fonctionne pas. En effet j'arrive à remplir la collection "listePointsVoyages" mais je n'arrive pas ensuite à la renseigner dans mon objet voyage. Ma ligne de code "voyage.listePoints = listePointsVoyage" bug au lancement de la macro et je finis avec le message d'erreur suivant :
1690539016014.png


Conclusion :

Je débute sur la manipulations des collections et des modules de classe et je n'arrive pas à faire ce premier exercice. Auriez-vous des idées pour me débloquer s'il vous plait ?

Je vous remercie tous par avance de l'attention que vous pourrez porter à ma demande

Une très bonne journée à tous et à bientôt sur le Forum

Guillaume
 

Pièces jointes

  • collectionVoyages.xlsm
    21.3 KB · Affichages: 7
Solution
Bonjour,
voila une piste!
j'ai rajouter un onglet Result!

j'ai pris les valeurs Arrivée & Départ pour charger l'intérieur du tableau pour bien montrer l'utilité des Dictionary Voyages & Points mais tu vas vite intégrer le principe!
VB:
Sub test()
Dim Voyages As Object, Points As Object, Col As Integer, Lin As Integer
Col = 3: Lin = 7
Set Voyages = CreateObject("scripting.dictionary")
Set Points = CreateObject("scripting.dictionary")

Sheets("Result").Cells.Delete
With Range("Tableau1")
    For i = 2 To .Rows.Count
      If Not Voyages.exists(CStr(.Cells(i, "A"))) Then
        Col = Col + 1
        Voyages(CStr(.Cells(i, "A"))) = Col
      End If
       If Not Points.exists(CStr(.Cells(i, "B"))) Then
        Lin = Lin + 1...

bsalv

XLDnaute Occasionnel
je ne comprend pas votre collection, moi, je préfère un dictionaire pour cela.
Code:
Public Dict_Trip As Object                   'ce dictionaire est "public" !!!

Sub Init()
     Dim aA
     Set Dict_Trip = CreateObject("scripting.dictionary")     'votre dictionaire des "trips"

     aA = Sheets("test").Range("A1").ListObject.DataBodyRange.Value     'lire ce tableau
     Dict_Trip(CStr(aA(1, 1))) = aA          'ajouter au dictionaire

End Sub

Sub Traitement()
     Dim aA
     If Dict_Trip Is Nothing Then Init

     aA = Dict_Trip("122300")                'récuperer les données de ce voyage
     MsgBox Join(Application.Transpose(Application.Index(aA, 0, 2)), vbLf), vbInformation, "TRIP " & aA(1, 1)     'par exemple une liste de tous les points de passage

End Sub
 

Pièces jointes

  • collectionVoyages.xlsm
    27.5 KB · Affichages: 4

Dranreb

XLDnaute Barbatruc
Bonjour.
Il faudrait Set voyage.listePoints = listePointsVoyage
À part ça, c'est marrant, si ça vous intéresse, j'ai une classe SsGr avec exactement comme propriétés Id As Variant et Co As Collection et quelques méthodes utiles, et une Function Gigogne qui renvoie une Collection de ces éléments.
Mais là je ne saurais pas trop quoi vous mettre comme démo avec vos données car il n'y a qu'un seul voyage et 19 points. Ce serait plus intéressant s'il y avait des points communs à plusieurs voyages, et surtout des informations associées à chaque paire dans des colonnes supplémentaires. Chaque membre de la Co du dernier niveau contient un tableau de variant reproduisant les valeurs de chaque ligne, et bien sûr chaque élément des Co des niveaux précédents est un SsGr.
 
Dernière édition:

GuillaumeFranceschi

XLDnaute Nouveau
je ne comprend pas votre collection, moi, je préfère un dictionaire pour cela.
Code:
Public Dict_Trip As Object                   'ce dictionaire est "public" !!!

Sub Init()
     Dim aA
     Set Dict_Trip = CreateObject("scripting.dictionary")     'votre dictionaire des "trips"

     aA = Sheets("test").Range("A1").ListObject.DataBodyRange.Value     'lire ce tableau
     Dict_Trip(CStr(aA(1, 1))) = aA          'ajouter au dictionaire

End Sub

Sub Traitement()
     Dim aA
     If Dict_Trip Is Nothing Then Init

     aA = Dict_Trip("122300")                'récuperer les données de ce voyage
     MsgBox Join(Application.Transpose(Application.Index(aA, 0, 2)), vbLf), vbInformation, "TRIP " & aA(1, 1)     'par exemple une liste de tous les points de passage

End Sub
Bonjour, merci beaucoup pour le coup de main avec une solution clé en main. Mais en fait je comprend rien de votre méthode. Je suis débutant. Pouvez-vous me détailler un petit peu plus la manière dont vous avez construit le dictionnaire ? Je pensais à préciser surtout ces points-ci :
  • Que permettent de faire les fonctions "ListObject" et "DataBodyRange" ?
  • Je ne suis pas entièrement certain de comprendre la ligne suivante :
Code:
Dict_Trip(CStr(aA(1, 1))) = aA
  • Mais j'imagine qu'en sortie du bodyrange, aA est une sorte de tableau donc le aA(1,1) est fait pour récupérer le numéro de voyage et ensuite on dit que le dictionnaire dont le nom est aA(1,1) (num de voyage donc) est égal à la liste totale de ma feuille test
  • Du coup si j'ai une liste de données avec plusieurs voyages il va falloir que je fasse une boucle sur tous les numéros existants de la liste et que je fasse un ajout de dictionnaire par numéro. Je pense comprendre cette partie mais il faut que je trouve une autre manière que celle que vous avez utilisée pour instancier "aA". En effet imaginons la liste de données suivante :
Regarde la pièce jointe 1175468
  • Là, pour instancier aA je ne peux plus utiliser Range("A1") car j'ai plusieurs numéros de voyages dans la même colonne. Que me conseilleriez-vous ? Peut-être que je pourrais faire un Range("A" & i & ":A" & j) où i et j sont le respectivement la ligne de début et la ligne de fin de la plage concernant mon deuxième voyage ?
Dans la procédure "Traitement" que vous avez faite, pouvez-vous m'en dire plus sur :
  • Que fait la fonction "Join" ?
  • Que fait l' "Application.Transpose" ? c'est pour remettre en colonne des informations qui étaient préalablement en format ligne ?
Merci beaucoup pour vos réponses et encore merci pour le coup de main

Guillaume
 

GuillaumeFranceschi

XLDnaute Nouveau
Bonjour.
Il faudrait Set voyage.listePoints = listePointsVoyage
À part ça, c'est marrant, si ça vous intéresse, j'ai une classe SsGr avec exactement comme propriétés Id As Variant et Co As Collection et quelques méthodes utiles, et une Function Gigogne qui renvoie une Collection de ces éléments.
Mais là je ne saurais pas trop quoi vous mettre comme démo avec vos données car il n'y a qu'un seul voyage et 19 points. Ce serait plus intéressant s'il y avait des points communs à plusieurs voyages, et surtout des informations associées à chaque paire dans des colonnes supplémentaires. Chaque membre de la Co du dernier niveau contient un tableau de variant reproduisant les valeurs de chaque ligne, et bien sûr chaque élément des Co des niveaux précédents est un SsGr.
Bonjoru Dranreb,

Merci pour votre retour, je n'ai pas tout compris mais je crois comprendre que ce que vous avez en tête peut fortement m'intéresser. Si je vous donne un fichier beaucoup plus exhaustif avec des colonnes supplémentaires et un plus grand nombre de voyages, cela pourrait-il vous convenir pour me montrer ce à quoi vous pensiez avec les méthodes que vous avez déjà mises en place ?
N'hésitez pas à me dire s'il manque quelque chose dans le fichier complet que j'ai joint à ce post

Dans l'attente de vous lire

Guillaume
 

Pièces jointes

  • listeExhaustiveVoyages.xlsm
    213.3 KB · Affichages: 4

GuillaumeFranceschi

XLDnaute Nouveau
Pouvez joindre le modèle avec plusieurs voyages différents ?
Une fois constituée que faudrait-il sortir de cette collection ?
Une fois constituée, j'aimerais la parcourir pour re disposer les données sous un autre format complétement différent dans une feuille excel. Mais j'espère ne pas avoir à vous importuner avec la manière dont je veux disposer mes données par la suite. En somme j'ai surtout besoin de comprendre comment ranger mes données dans des collections propres pour ensuite venir lire mes collections et consommer mes données comme je le souhaite. En sachant qu'en terme d'organisation (mais ça, vu votre réponse précédente vous avez l'air de l'avoir très bien compris) je veux réussir à ranger mes données par objet "Voyage" qui eux même contiennent une liste de points. Ce qui me permettra par la suite d'appeler des voyages de ma collection et de consommer tout ce que j'ai rangé à l'intérieur (ma liste de point notamment).
Merci beaucoup
 

GuillaumeFranceschi

XLDnaute Nouveau
Voilà un exemple
Je viens d'ouvrir le fichier, merci beaucoup pour l'aide, malheureusement je comprend très peu de choses à la manière dont ça a été codé. Notamment la fonction Gigogne qui est à la base de votre formatage de données en collection, je ne comprend pas du tout comment elle est codée, la plupart des notions me sont inconnues :
  • La fonction semble avoir deux paramètres en entrée alors que quand vous l'appelez dans "Démo" elle en contient 3
  • "TableauAcquis Tdon" m'est inconnu
  • Index par fusion aussi
  • en fait je ne comprend juste pas la manière dont ce code arrive à faire une collection
Je vais essayer de me plonger dedans mais très peu de chance que je puisse l'exploiter. Merci quand même

PS : est-ce qu'un livre ou un site sur les classe et collection pourrait m'être utile pour commencer ? De votre côté comment avez-vous acquises les compétences nécessaires à la création de ce genre de code ?
 

Dranreb

XLDnaute Barbatruc
C'est parce que le second paramètre est un ParamArray, c'est à dire une série de paramètres en nombre indéterminé qui se récupère sous forme d'un tableau dans la procédure.
TableauAquis est une Function du même module qui s'accommode de plein de sortes de sources pour en déduire toujours un tableau dynamique.
IndexerParFusion est une Sub du même module qui établit un tableau de Long des numéros de lignes rangé par ordres de valeurs des contenus. C'est en somme un classement qui, au lieu de changer l'ordre des données, indique plutôt dans quel ordre il faut les parcourir.
C'est sûr que dans la fonction gigogne, l'instruction qui fabrique la collection est très discrète, puisque c'est juste la Set Gigogne = SousGroupes(1)
La SousGroupe, par contre, elle est récursive et pas piquée des hannetons !
 

bsalv

XLDnaute Occasionnel
un bon lien pour ces dictionaires https://excelmacromastery.com/vba-dictionary/
un exemple vite fait avec votre fichier, un dictionaire qui lit ce tableau et crée vos 157 differents voyages.
2ième dictionaire, comme démo, qui lit ces 157 voyages et crée une liste de tous les voyages qui passent un point (en feuille points) (il n'y a que 26 points différents) et un voyage passe plusieurs fois le même point.
 

Pièces jointes

  • listeExhaustiveVoyages.xlsm
    226.5 KB · Affichages: 3

Discussions similaires

Réponses
29
Affichages
920

Statistiques des forums

Discussions
312 207
Messages
2 086 238
Membres
103 162
dernier inscrit
fcfg