Une fonction qui "ordonne" les clé d'un dictionnaire

Résolu
wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   -  
pijaku Messages postés 13513 Date d'inscription   Statut Modérateur Dernière intervention   -
Bonjour.
Je travail sous VBA Excel.
J'utilise un dictionnaire pour obtenir une liste sans doublons.
J'ai créé mon dico ainsi :
Set Mon_Dico = CreateObject("Scripting.Dictionary")

Puis j'ai remplis mon dico :
TABLEAU = Sheets("Feuil1").Range("A2", "A100")
For I = 1 To UBound(TABLEAU, 1)
    Mon_Dico(Tableau_SITE(I, 1)) = ""    
Next I


Maintenant je souhaite pouvoir ordonner les clés de mon dictionnaire :

Je suis en train de créer une fonction "Ordonne_Dico" ... mais je n'y arrive pas.

J'appelle ma fonction :
Mon_Dico = Ordonne_Dico(Mon_Dico)


Et j'ai crée ma fonction :

Function Ordonne_Dico(Dico) ' Ordonne le dictionnaire par sa key
ListeCle = Dico.Keys
ListeElement = Dico.Items

    For I = 0 To Dico.Count - 2
 
        For k = I + 1 To Dico.Count - 1
 
            If ListeCle(I) > ListeCle(k) Then
 
                Tempo_Cle = ListeCle(k)
                Tempo_Element = ListeElement(k)
 
                ListeElement(k) = ListeElement(I)
                ListeCle(k) = ListeCle(I)
 
                ListeCle(I) = Tempo_Cle
                ListeElement(I) = Tempo2_Element
 
            End If
 
        Next k
 
    Next I

Set Ordonne_Dico = CreateObject("Scripting.Dictionary")
Ordonne_Dico.Keys = ListeCle
Ordonne_Dico.Items = ListeElement

End Function


ça ne marche pas.
Je ne parviens pas à récupérer mon dictionnaire ordonné en fin de fonction !!
Quelqu'un aurait déjà fait quelque-chose de similaire ??

Merci

6 réponses

  1. ThauTheme Messages postés 1564 Statut Membre 160
     
    Bonjour WireLess, bonjour le forum,

    Honteusement pompé sur le site de Jacques Boisgontier (tant pis si je me fait engueuler pour ce genre de citation) :

    Sub Tri(a, gauc, droi) ' Quick sort
      ref = a((gauc + droi) \ 2)
      g = gauc: d = droi
      Do
        Do While a(g) < ref: g = g + 1: Loop
        Do While ref < a(d): d = d - 1: Loop
        If g <= d Then
          tmp = a(g): a(g) = a(d): a(d) = tmp
          g = g + 1: d = d - 1
        End If
      Loop While g <= d
      If g < droi Then Call Tri(a, g, droi)
      If gauc < d Then Call Tri(a, gauc, d)
    End Sub


    à la fin de la boucle du dictionnaire tu écris :
    Temp = Mon_Dico.Keys
    Call Tri(Temp,Lbound(TMP,1),Ubound(TMP,1))
    0
    1. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5
       
      Bon ...
      J'en arrive à la conclusion qu'on ne peut pas utiliser une fonction pour ordonner un dictionnaire ...

      C'est dommage parce que j'ai plusieurs dictionnaire à ordonner ... et de passer par une fonction, il me semblait que c'était le moyen le plus propre.

      Mais quand même ... si quelqu'un a une idée magique ...
      Mon problème dans le code ci-dessous, c'est que la ligne 27 de mon code ... rappel le début de ma fonction ... (et la boule est bouclé pour ainsi dire.)

      Function Ordonne_Dico(Dico) As Variant ' Ordonne le dictionnaire par sa key
      ListeCle = Dico.Keys
      ListeElement = Dico.Items
        
          For I = 0 To Dico.Count - 2
       
              For k = I + 1 To Dico.Count - 1
       
                  If ListeCle(I) > ListeCle(k) Then
       
                      Tempo_Cle = ListeCle(k)
                      Tempo_Element = ListeElement(k)
       
                      ListeElement(k) = ListeElement(I)
                      ListeCle(k) = ListeCle(I)
       
                      ListeCle(I) = Tempo_Cle
                      ListeElement(I) = Tempo2_Element
       
                  End If
       
              Next k
       
          Next I
      
      For I = 1 To UBound(ListeCle, 1)
          Ordonne_Dico(ListeCle(I)) = ""
      Next I
      
      End Function


      Merci tout de même à tout ceux qui ont répondu à mon sujet (Thautheme ; f894009 ).
      Si personne n'a d'idées magique ... je clôturerais le sujet .
      Merci
      0
    2. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5
       
      Je ne comprend pas ThauTheme.

      A la fin de ton script
      Temp = Mon_Dico.Keys
      Call Tri(Temp,Lbound(Temp,1),Ubound(Temp,1))

      Je ne peux pas récupérer Temp trié à partir d'un "sub" ??
      Il faut forcement que je passe par une fonction ?? non ???
      0
      1. ThauTheme Messages postés 1564 Statut Membre 160 > wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention  
         
        Ben le sub c'est TRI(a, gauc, droi) et c'est pas une fonction... Il te suffit de lancer le Sub à chaque dictionnaire... C'est moi qui ne comprends pas où est le problème pour toi ?
        0
      2. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5 > ThauTheme Messages postés 1564 Statut Membre
         
        :-/
        Ben ... mon dico sera bien trié, mais ce dictionnaire n'existera qu'à l'intérieur de mon sub TRI(a, gauc, droi) !!
        Il n'existera pas à l'extérieur de ce "sub" !
        Si je veux le faire remonter dans mon sub, il faut soit que j'utilise une fonction, soit que je déclare mon dico en variable global :-/ (ce qui est impossible pour les dico dans les user form :-/ )
        Voilou mon problème !!
        A moins que je ne sois complètement fou !!
        0
  2. ThauTheme Messages postés 1564 Statut Membre 160
     
    Ben non ! Ton dico est définit et déclaré dans ton premier sub... La fonction Tri ne sert qu'à trier un tableau et un dictionnaire est un tableau !... C'est pour cela que je te disais d'ajouter, après la création de ton dictionnaire, les lignes qui vont le trier. Si je reprends ton exemple ça donne :

    Sub Macro1()
    Set Mon_Dico = CreateObject("Scripting.Dictionary")
    TABLEAU = Sheets("Feuil1").Range("A2", "A100")
    For I = 1 To UBound(TABLEAU, 1)
        Mon_Dico(Tableau_SITE(I, 1)) = ""
    Next I
    'lignes pour trier
    TEMP = Mon_Dico.keys
    Call Tri(TEMP, LBound(TEMP), UBound(TEMP))
    'ligne pour utiliser le tri
    Me.listbox1.List = TEMP 'par exemple pour alimenter une ListBox avec une liste triée sans doublon
    End Sub
    
    Sub Tri(a, gauc, droi) ' Quick sort
    ref = a((gauc + droi) \ 2)
    g = gauc: d = droi
    Do
        Do While a(g) < ref: g = g + 1: Loop
        Do While ref < a(d): d = d - 1: Loop
        If g <= d Then
            tmp = a(g): a(g) = a(d): a(d) = tmp
            g = g + 1: d = d - 1
        End If
    Loop While g <= d
    If g < droi Then Call Tri(a, g, droi)
    If gauc < d Then Call Tri(a, gauc, d)
    End Sub


    0
    1. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5
       
      https://www.cjoint.com/c/EGpqgUkZzvN

      Je viens d'essayer dans le fichier que tu m'avais joint jeudi ...
      Ben ça marche pas ... Le dictionnaire trié existe bien, mais uniquement dans le sub Tri.
      Ce dictionnaire ne "remonte" pas dans le programme qui appel "Tri" :-/

      (ou alors, c'est vraiment moi qui comprends rien ... je doute ... )
      0
  3. ThauTheme Messages postés 1564 Statut Membre 160
     
    Re, j'ai l'impression que tu ne regardes même pas les codes ! Lignes 8 à 11 du dernier code que je t'ai proposé C'est la variable TEMP qui est triée pas le dictionnaire !...
    0
    1. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5
       
      Fuuuuuuuuuuhhhhh !!!
      Je suis dyslexique du code !!!
      Alors celui-là !! j'l'avais pas vu ! dsl de t'avoir fait perdre ton temps!
      ça marche super bien à présent :-/
      0
  4. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  5. pijaku Messages postés 13513 Date d'inscription   Statut Modérateur Dernière intervention   2 773
     
    Bonjour,

    Je réagit juste à ceci :
    ce qui est impossible pour les dico dans les user form
    Ma question : pourquoi désires-tu utiliser des objets dictionary dans un UserForm?
    La première réponse qui me vienne à l'esprit : le remplissage d'une liste (ComboBox ou ListBox).
    Si oui, il ne m'apparait pas opportun d'utiliser des variables gourmandes comme les object dictionary pour se faire.
    Tu dis...
    0
    1. pijaku Messages postés 13513 Date d'inscription   Statut Modérateur Dernière intervention   2 773
       
      Pour argumenter mon propos, voici le remplissage d'une combobox triée et sans doublon :
      Private Sub UserForm_Initialize()
      Dim TABLEAU As Variant, I As Long
      
      TABLEAU = Sheets("Feuil1").Range("A2:A33")
      Call Tri(TABLEAU, LBound(TABLEAU), UBound(TABLEAU))
      
      For I = 1 To UBound(TABLEAU, 1)
          ComboBox1.Value = TABLEAU(I, 1)
           'on évite les doublons
          If ComboBox1.ListIndex = -1 Then ComboBox1.AddItem TABLEAU(I, 1)
      Next I
      End Sub
      
      Sub Tri(a, gauc, droi) ' Quick sort
      Dim ref, g, d, tmp
      'Comme remplissage du tableau initial par l'objet Range
      'il convient d'ajouter la dimension ( , 1) sous peine d'erreur 9
        ref = a((gauc + droi) \ 2, 1)
        g = gauc: d = droi
        Do
          Do While a(g, 1) < ref: g = g + 1: Loop
          Do While ref < a(d, 1): d = d - 1: Loop
          If g <= d Then
            tmp = a(g, 1): a(g, 1) = a(d, 1): a(d, 1) = tmp
            g = g + 1: d = d - 1
          End If
        Loop While g <= d
        If g < droi Then Call Tri(a, g, droi)
        If gauc < d Then Call Tri(a, gauc, d)
      End Sub
      0
    2. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5
       
      Salut Pikaju!
      Moi, j'avoue que utiliser des dictionnaires ... ou des ComboBox1.AddItem ...
      J'avais juste besoin d'une méthode qui marche.

      Par contre, ce qui m'a rendu dingue avec le script "ComboBox1.AddItem" c'est les déclenchement intempestif de combobox1_Click ... Fuuuuuhhhh ... Je me suis réellement rendu fou avec les Comobox.enabled = True/False ... jusqu'à qu'on me propose la méthode des dictionnaires.

      Sinon, merci beaucoup pour ton aide.
      0
      1. pijaku Messages postés 13513 Date d'inscription   Statut Modérateur Dernière intervention   2 773 > wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention  
         
        Hum hum...
        Change() je veut bien, à la rigueur, mais Click()? Tu es sur de toi?

        Pour tester : un userform, une combobox et le code suivant :
        Private Sub ComboBox1_Change()
        MsgBox "Change"
        End Sub
        
        Private Sub ComboBox1_Click()
        MsgBox "Clic"
        End Sub
        
        Private Sub UserForm_Initialize()
        Dim TABLEAU As Variant, I As Long
        
        TABLEAU = Sheets("Feuil1").Range("A2:A6")
        Call Tri(TABLEAU, LBound(TABLEAU), UBound(TABLEAU))
        
        For I = 1 To UBound(TABLEAU, 1)
            ComboBox1.Value = TABLEAU(I, 1)
             'on évite les doublons
            If ComboBox1.ListIndex = -1 Then ComboBox1.AddItem TABLEAU(I, 1)
        Next I
        End Sub
        
        Sub Tri(a, gauc, droi) ' Quick sort
        Dim ref, g, d, tmp
        'Comme remplissage du tableau initial par l'objet Range
        'il convient d'ajouter la dimension ( , 1) sous peine d'erreur 9
          ref = a((gauc + droi) \ 2, 1)
          g = gauc: d = droi
          Do
            Do While a(g, 1) < ref: g = g + 1: Loop
            Do While ref < a(d, 1): d = d - 1: Loop
            If g <= d Then
              tmp = a(g, 1): a(g, 1) = a(d, 1): a(d, 1) = tmp
              g = g + 1: d = d - 1
            End If
          Loop While g <= d
          If g < droi Then Call Tri(a, g, droi)
          If gauc < d Then Call Tri(a, gauc, d)
        End Sub


        Teste et dis moi...
        0
  6. pijaku Messages postés 13513 Date d'inscription   Statut Modérateur Dernière intervention   2 773
     
    Bonjour,

    Pour en revenir à la question initiale, il est possible de faire une fonction perso dans le but d'ordonner un dictionary ET de récupérer, via cette fonction, un objet Dictionary.

    Code de la fonction : [honteusement pompé sur le code de wire less...]
    Function Ordonne_Dico(Dico As Object, Optional ByElem As Boolean) As Object
    Dim ListeCle(), ListeElement(), I As Long, k As Long, Tempo_Cle, Tempo_Element
    
       ListeCle = Dico.Keys
       ListeElement = Dico.Items
       If ByElem Then
          For I = 0 To Dico.Count - 2
             For k = I + 1 To Dico.Count - 1
                If ListeElement(I) > ListeElement(k) Then
                   Tempo_Cle = ListeCle(k)
                   Tempo_Element = ListeElement(k)
                   ListeElement(k) = ListeElement(I)
                   ListeCle(k) = ListeCle(I)
                   ListeCle(I) = Tempo_Cle
                   ListeElement(I) = Tempo_Element
                End If
             Next k
          Next I
       Else
          For I = 0 To Dico.Count - 2
             For k = I + 1 To Dico.Count - 1
                If ListeCle(I) > ListeCle(k) Then
                   Tempo_Cle = ListeCle(k)
                   Tempo_Element = ListeElement(k)
                   ListeElement(k) = ListeElement(I)
                   ListeCle(k) = ListeCle(I)
                   ListeCle(I) = Tempo_Cle
                   ListeElement(I) = Tempo_Element
                End If
             Next k
          Next I
       End If
       Set Ordonne_Dico = CreateObject("Scripting.Dictionary")
       For I = LBound(ListeCle) To UBound(ListeCle)
          Ordonne_Dico.Add ListeCle(I), ListeElement(I)
       Next I
    End Function


    Cette fonction laisse à l'utilisateur le choix de trier par la clé ou par les éléments.
    Pour l'appeler :
    1- par la clé :
    Sub test_ByCle()
    Dim Mon_Dico As Object, TABLEAU, I As Long, Elem
    
       Set Mon_Dico = CreateObject("Scripting.Dictionary")
       TABLEAU = Sheets("Feuil1").Range("A1:A20")
       For I = 1 To UBound(TABLEAU, 1)
          Mon_Dico(TABLEAU(I, 1)) = ""
       Next I
       Set Mon_Dico = Ordonne_Dico(Mon_Dico) 'ByElem Optional = False
       '----------------------------------------Vérification :
       For Each Elem In Mon_Dico.Keys
          Debug.Print Mon_Dico(Elem) & " - " & Elem
       Next
    End Sub

    2- par les éléments:
    Sub test_ByElem()
    Dim Mon_Dico As Object, TABLEAU, I As Long, Elem
    
       Set Mon_Dico = CreateObject("Scripting.Dictionary")
       TABLEAU = Sheets("Feuil1").Range("A1:A20")
       For I = 1 To UBound(TABLEAU, 1)
          Mon_Dico(TABLEAU(I, 1)) = I
       Next I
       Set Mon_Dico = Ordonne_Dico(Mon_Dico, True) 'ByElem = True
       '----------------------------------------Vérification :
       For Each Elem In Mon_Dico.Keys
          Debug.Print Mon_Dico(Elem) & " - " & Elem
       Next
    End Sub


    C'est juste pour le fun, bien entendu!
    0
    1. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5
       
      Bien entendu ...
      Non non non ... pas "bien entendu" ... C'est juste de la bombe ce que tu viens de faire !!
      C'est pas du "Fun" C'est de l'art ! :-D
      La voila la fonction magique qui ordonne un dictionnaire. Tu viens de la créer!
      Si tu as le n° de téléphone de Jacques Boisgontier je l'appel tout de suite pour qu'il rajoute cette fonction à son site!

      C'était le chaînon manquant ... c'est comme si tu venais de démontrer l'existence de la masse noir de l'univers.
      Voila ! On peut enfin créer une combo-box proprement ... sans doublons et ordonné à partir d'un dictionnaire !! C'est pas rien.
      C'est fantastique !!

      Et je mesure juste toute les lacune que j'ai !!

      pourquoi :
         
      For I = LBound(ListeCle) To UBound(ListeCle)
      Ordonne_Dico.Add ListeCle(I), ListeElement(I)
      Next I
      

      Et non pas :
      For I = LBound(ListeCle) To UBound(ListeCle)
          Ordonne_Dico(ListeCle(I)) = ListeElement(I)
      Next I


      Et tant d'autres questions ...
      Merci encore.
      (Ps, je n'ai pas eu le temps de tester le script que tu m'as proposé plus haut ... mais je ne doute pas qu'il doit marcher ... j'essayerais plus tard ... j'ai encore trop de trucs à "débugger" dans mon code ...
      (si tu as un peu de temps à perdre, tu peux peut-être répondre aux quelques questions que j'ai posé là :
      https://forums.commentcamarche.net/forum/affich-32212197-combobox-et-listindex#p32224355
      même si c'est juste des questions de curiosités )
      Sinon :

      For i = 1 to 1000
      Call Msgbox(" MERCI ")
      Next i


      Bon, j'exagère peut-être un peu !! Mais en tout cas, merci !
      A+
      0
    2. pijaku Messages postés 13513 Date d'inscription   Statut Modérateur Dernière intervention   2 773 > wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention  
       
      Bonjour,

      pourquoi :
      For I = LBound(ListeCle) To UBound(ListeCle)
      Ordonne_Dico.Add ListeCle(I), ListeElement(I)
      Next I


      Et non pas :
      For I = LBound(ListeCle) To UBound(ListeCle)
          Ordonne_Dico(ListeCle(I)) = ListeElement(I)
      Next I


      ===> Aucune différence. C'est toi qui choizz

      Voila ! On peut enfin créer une combo-box proprement ... sans doublons et ordonné à partir d'un dictionnaire !! C'est pas rien.

      Ben, en fait, on pouvait déjà! Et sans dictionary! Cf mes codes d'hier.
      Ce qui est encore mieux...
      0
    3. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5
       
      Pia pia pia ... oui ... mais moi j'avais pas réussi ... promis je regarderais le pourquoi de mes "combobox1_Click" intempestif ... c'est peut-être juste que j'avais codé avec mes pieds ce qui est aussi très possible, je ne suis pas encore un master du combobox comme toi :-p.
      sinon, chez moi
      For I = LBound(ListeCle) To UBound(ListeCle)
          Ordonne_Dico(ListeCle(I)) = ListeElement(I)
      Next I

      Ben ça ne marche pas ... Ben ... ça ne veux pas dire qu'en général ça ne marche pas ... c'était peut-être un autre beugge à côté ... m'enfin ... je suis content d'avoir un code qui marche ... et qui est super rapide en plus.
      Thx !
      0
    4. pijaku Messages postés 13513 Date d'inscription   Statut Modérateur Dernière intervention   2 773 > wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention  
       
      Ou c'est peut-être que comme un idiot, j'ai mélangé les Items et les Keys...
      Essaye juste en changeant :
       Ordonne_Dico(ListeElement(I)) = ListeCle(I)

      et si cela fonctionne c'est que j'ai fait l'échange quelque part...
      0
    5. wire less Messages postés 247 Date d'inscription   Statut Membre Dernière intervention   5 > pijaku Messages postés 13513 Date d'inscription   Statut Modérateur Dernière intervention  
       
      :-/ non non ... c'était bien Ordonne_Dico(ListeCle(I)) = ListeElement(I)
      0