Microsoft 365 Créer un dictionnaire sans doublon

Goufra

XLDnaute Occasionnel
Bonjour à tous,

je suis l’hérétique de service, J’ai créé un dictionnaire avec doublons !
Je souhaite à partir de ce dictionnaire créer un dictionnaire sans doublon

C’est pour le fun je sais le créer à partir d’une plage

Donc voici le code qui ne fonctionne pas


Sub nefonctionnepas() '
For Each c In DavecDoublons.Keys

DsansDoublon(c.Value) = ""

MsgBox c

Next c
End Sub

pièce jointe

C’est surtout pour faire marcher ma boîte à malices et éviter qu’elle ne se grippe ! Et je cherche à comprendre le fonctionnement d'un dictionnaire.
Avec mes sincères remerciements anticipés .
Goufra
 

Pièces jointes

  • 0000 Goufra.xlsm
    31.2 KB · Affichages: 20

patricktoulon

XLDnaute Barbatruc
Bonjour
le fonctionnement d'un dictionnaire est on ne peut plus simple
un dictionnaire a des clés et des items (valeur : numerique,string ou même object)

quand on ajoute un element dans le dictionnaire on le fait par sa clé
exemple
dictionnaire("toto")="ce que tu veux"

maintenant si je fait
dictionnaire("toto")="autre chose"

en fait ici on a changé la valeur de l'item du dictionnaire pas sa clé il n'y a donc toujours q'une seule clé ce qui veux dire pas de doublons


le dictionnaire a des méthode comme par exemple la fonction exists
if not dictionnaire.exists("toto") then dictionnaire("toto")="trucbidule"
a
vec cette méthode cela permet de ne plus y toucher dans une boucle par exemple

oserais-je dire qu'il existe deux autres objects plus ancien mais encore fonctionels dans windows
l'object (arraylist)
et
l'object("sortedlist")

qui ont d'autre fonctions et méthode intégrées tel que le tri dans un ordre ainsi que le deplacement dans la hiérachie etc..etc..

pour le dictionnaire
on peut le transformer par la suite en deux array distincts et exploitables
macolonneclé=application.transpose(dictionnaire.keys)
macolonnevaleur=application.transpose(dictionnaire.items)
ces deux variable sont des array contenant les valeurs et clé (sans doublons bien sur )

pour en apprendre un peu plus et mettre tout cela en pratique je te renvoie vers une page de notre regretté Jacques Boisgontier

je pense qu'il y a suffisamment d'exemples et mise en pratique pour maitriser ou apprendre à maitriser le dictionnaire

l'object dictionnaire de Jacques Boisgontier

je t'invite aussi a faire des recherche sur l'object "Collection" natif dans vba
qui peut apporter une solution simple au problème doublons

Bonne lecture ;)
 

mapomme

XLDnaute Barbatruc
Supporter XLD
Bonjour @Goufra :),

je suis l’hérétique de service, J’ai créé un dictionnaire avec doublons !
Par l'implémentation de l'objet "Dictionary", une clef est unique.
Donc construire un "Dictionary "avec plusieurs clefs identiques n'est pas possible.

Votre "Dictionary" est forcément sans doublon. C'est donc la conclusion que vous tirez du résultat de votre code qui est fausse. Vous n'avez pas construit de "Dictionary" avec doublons.

Le classeur que vous avez fourni est un peu fouillis.
J'ai considéré votre procédure : Dictionnaire3()

Quand on construit un dictionary, il faut s'interroger sur ce qu'est la clef et ce qu'est l'item associé.
Votre dictionary doit comporter en clef les valeurs de la colonne 2 du tableau structuré 'Tableau1" et comme élément associé les valeurs de la colonne 3 du tableau structuré "Tableau1".

Vous remarquerez que j'ai bien mentionné valeur et pas cellules.

Considérons la ligne d'insertion dans votre dictionary :
D.Add ActiveCell(i, 2), ActiveCell(i, 3).Value

La clef est ActiveCell(i, 2). ActiveCell(i, 2) n'est pas une valeur mais un objet Range (cellule, monoplage ou multiplage). En l'occurrence ici c'est une cellule et non pas la valeur de la cellule.
Au sein de la boucle i, quand i varie, ActiveCell(i, 2) sera à chaque fois une cellule différente de la précédente (i-1) et différente de la cellule suivante (i+1). Et ceci est vrai quelque soient les valeurs de ces cellules (identiques ou non).

Donc vous remplissez votre dictionnaire avec des clefs toutes différentes (autant que de cellules dans la colonne 2 de votre tableau). Votre dictionary ne comporte aucun doublon au niveau des clefs.

Si vous remplacez votre ligne de code par : D.Add ActiveCell(i, 2).Value, ActiveCell(i, 3).Value,
VBA vous avertira de la présence de doublon car la clef est la valeur des cellules et non pas l'objet cellule lui-même.

Remarque : l'erreur peut aussi être induite par le fait que la valeur par défaut d'un range cellule est la valeur de la cellule et souvent on confond les deux.
La plupart du temps quand on écrit : X=Cells(1,1), X va contenir la valeur de la cellule X (ça marche aussi pour un range monoplage). Car VBA sait que le signe = correspond à la valeur. Si on considère l'objet range lui-même, on écrira Set X=Cells(1,1), VBA sait alors que l'emploi du SET correspond à l'objet range et non à la valeur du range.

Mais l'objet dictionary accepte comme clef différents types de variables.
Quand on insère une clef X dans un dictionary, la clef sera du type de X.
Si on lui donne comme clef un range, il considérera que la clef est le range et non pas sa valeur.

Concernant votre procédure, vous avez déjà le tableau des valeurs : T1 = Range("tableau1")
Inutile d'aller sur la feuille de calcul pour lire les cellules, les sélectionner, etc (en plus cela prend du temps d'exécution).

Voir module1. Cliquez sur le bouton Hop!

Code dans module1 :
VB:
Option Explicit

Dim D As New Scripting.Dictionary

Sub Dictionnaire3()
Dim T, i&
   D.RemoveAll                'on vide le dictionary
   T = Range("tableau1")      'on lit les valeurs du tableau structuré "tableau1"
   For i = 1 To UBound(T)     'boucle sur les lignes de T
      If Not D.Exists(T(i, 2)) Then D.Add T(i, 2), T(i, 3)
   Next i
 
   Range(Range("G7"), Range("G7").End(xlDown)).Resize(, 2).Clear     'on efface les anciens résultats
   Range("G7").Resize(D.Count, 1) = Application.Transpose(D.Keys)    'les clefs
   Range("H7").Resize(D.Count, 1) = Application.Transpose(D.Items)   'les items
End Sub
 

Pièces jointes

  • Goufra- dictionary- v1.xlsm
    20.2 KB · Affichages: 17
Dernière édition:

Goufra

XLDnaute Occasionnel
Bonjour @Goufra :),


Par l'implémentation de l'objet "Dictionary", une clef est unique.
Donc construire un "Dictionary "avec plusieurs clefs identiques n'est pas possible.

Votre "Dictionary" est forcément sans doublon. C'est donc la conclusion que vous tirez du résultat de votre code qui est fausse. Vous n'avez pas construit de "Dictionary" avec doublons.

Le classeur que vous avez fourni est un peu fouillis.
J'ai considéré votre procédure : Dictionnaire3()

Quand on construit un dictionary, il faut s'interroger sur ce qu'est la clef et ce qu'est l'item associé.
Votre dictionary doit comporter en clef les valeurs de la colonne 2 du tableau structuré 'Tableau1" et comme élément associé les valeurs de la colonne 3 du tableau structuré "Tableau1".

Vous remarquerez que j'ai bien mentionné valeur et pas cellules.

Considérons la ligne d'insertion dans votre dictionary :
D.Add ActiveCell(i, 2), ActiveCell(i, 3).Value

La clef est ActiveCell(i, 2). ActiveCell(i, 2) n'est pas une valeur mais un objet Range (cellule, monoplage ou multiplage). En l'occurrence ici c'est une cellule et non pas la valeur de la cellule.
Au sein de la boucle i, quand i varie, ActiveCell(i, 2) sera à chaque fois une cellule différente de la précédente (i-1) et différente de la cellule suivante (i+1). Et ceci est vrai quelque doit les valeurs de ces cellules (identiques ou non).

Donc vous remplissez votre dictionnaire avec des clefs toutes différentes (autant que de cellules dans la colonne 2 de votre tableau). Votre dictionary ne comporte aucun doublon au niveau des clefs.

Si vous remplacez votre ligne de code par : D.Add ActiveCell(i, 2).Value, ActiveCell(i, 3).Value,
VBA vous avertira de la présence de doublon car la clef est la valeur des cellules et non pas l'objet cellule lui-même.

Remarque : l'erreur peut aussi être induite par le fait que la valeur par défaut d'un range cellule est la valeur de la cellule et souvent on confond les deux.
La plupart du temps quand on écrit : X=Cells(1,1), X va contenir la valeur de la cellule X (ça marche aussi pour un range monoplage). Car VBA sait que le signe = correspond à la valeur. Si on considère l'objet range lui-même, on écrira Set X=Cells(1,1), VBA sait alors que l'emploi du SET correspond à l'objet range et non à la valeur du range.

Mais l'objet dictionary accepte comme clef différents types de variables.
Quand on insère une clef X dans un dictionary, la clef sera du type de X.
Si on lui donne comme clef un range, il considérera que la clef est le range et non pas sa valeur.

Concernant votre procédure, vous avez déjà le tableau des valeurs : T1 = Range("tableau1")
Inutile d'aller sur la feuille de calcul pour lire les cellules, les sélectionner, etc (en plus cela prend du temps d'exécution).

Voir module1. Cliquez sur le bouton Hop!

Code dans module1 :
VB:
Option Explicit

Dim D As New Scripting.Dictionary

Sub Dictionnaire3()
Dim T, i&
   D.RemoveAll                'on vide le dictionary
   T = Range("tableau1")      'on lit les valeurs du tableau structuré "tableau1"
   For i = 1 To UBound(T)     'boucle sur les lignes de T
      If Not D.Exists(T(i, 2)) Then D.Add T(i, 2), T(i, 3)
   Next i
 
   Range(Range("G7"), Range("G7").End(xlDown)).Resize(, 2).Clear     'on efface les anciens résultats
   Range("G7").Resize(D.Count, 1) = Application.Transpose(D.Keys)    'les clefs
   Range("H7").Resize(D.Count, 1) = Application.Transpose(D.Items)   'les items
End Sub
merci à vous je prends le temps de lire et de comprendre vos explications pour revenir vers vous.
 

Goufra

XLDnaute Occasionnel
Bonjour Patrick et ma Pomme

A nouveau mes remerciements à tous les deux qui m’avez décortiqué le fonctionnement du dictionnaire et amélioré la compréhension.

En ce qui concerne je suis grandement isolé.
Je connais le site de J.Boisgonthier, je le pratique depuis longtemps.

J’ai donc envoyé ma bouteille dans l’océan de excel downloads pour arriver à comprendre le pourquoi du comment. Vous m’avez permis de progresser

Bref par un temps pluvieux il est agréable d’avoir un petit coin de ciel bleu.

Si j’osais

J’ai 3 bouquins sur excel dont 2 sur vba mais rien sur les dictionnaires, je suis preneur d’un titre qui explique notamment le pourquoi du comment.

Exemple dans le code :
DsansDoublon(c.Value) = ""

J’applique mais je ne comprends pas = les guillemets

Bonne soirée à vous
Bien cordialement
Goufra
 

mapomme

XLDnaute Barbatruc
Supporter XLD
sansDoublon(c.Value) = ""
Dans ce cas, la clef est la valeur de la cellule c et l'élément (Item) associé à la clef est la chaine de caractère vide notée ""
  • "toto" représente la chaine de caractère toto (4 caractères)
  • "t" représente la chaine de caractère t (1 caractère)
  • "" représente la chaine de caractère vide (0 caractère)
On utilise cela souvent pour trouver les valeurs uniques.
Exemple : Trouver les valeurs uniques des cellules de la plage A1 à A100.
Les valeurs uniques sont compris dans dico.keys, le nombre de valeur unique est dico.Count.

VB:
Sub test()
Dim i As Long, dico As Object
   Set dico = CreateObject("scripting.dictionary")
   For i = 1 To 100
      If Cells(i, "a") <> "" Then dico(Cells(i, "a").Value) = ""
   Next i
   Columns("c:c").ClearContents
   If dico.Count > 0 Then Range("c1").Resize(dico.Count) = Application.Transpose(dico.keys)
End Sub

Quand on utilise la notation dico(clefx) = itemx, que fait le dictionary ?
  • si la clef existe déjà, il remplace l'ancien item de la clef clefx par l'item itemx.
  • si la clef n'existe pas, il crée l’élément de clef clefx et ensuite seulement lui attribue l'item itemx
 
Dernière édition:

Goufra

XLDnaute Occasionnel
Bonjour
Merci pour votre réponse Ma pomme

Ce code je l'ai pris sur le site de j.gonthier. C'est le premier code développé.

For Each c In plage
DsansDoublon(c.Value) = ""
Next c

Je suis navré mais je dois avoir atteint mon plafond de verre, je ne comprends pas votre explication.

Une autre instruction que je ne comprends pas.
Range(Range("G7"), Range("G7").End(xlDown)).Resize(, 2).Clear
End(xldown)
Si 10 inscriptions, seules les 10 lignes sont effacées et non celles servies dessous. Il n'efface donc pas jusqu'à la fin des deux colonnes concernées.

Je vous remercie pour votre patience
bien cordialement
Goufra
 

mapomme

XLDnaute Barbatruc
Supporter XLD
For Each c In plage
DsansDoublon(c.Value) = ""
Next c
Je ne renonce pas aussi facilement !


Un dictionary (qu'on nommera dic) est une structure qui possède des clefs et des éléments. A chaque clef est associé un élément (Item). Au sein du dictionary, il ne peut y avoir deux fois la même clef.

Pour mettre un élément (clef + item associé) dans un dictionary, on peut utiliser deux manières de faire.
La première est (la plus logique selon moi) :
dic.add clef, item ( ex: dic.add "Voiture","Citroën" )

Si la clef "Voiture" est déjà présente, alors dic.add "Voiture","Citroën" aboutira à une erreur dans le programme.
En général, on écrit donc :
if not dic.exists("Voiture") then dic.add "Voiture","Citroën" (on évite ainsi l'erreur de vouloir ajouter une clef déjà existante)

La seconde est :
dic(clef)=item ( ex : dic("Voiture")="Citroën" )

Si la clef existe, la méthode add va rechercher l'enregistrement de clef "Voiture" et place dans l'Item correspondant la nouvelle valeur "Citroën" (cela écrase la précédente valeur associée à la clef "Voiture")
Si la clef n'existe pas, alors cette méthode crée un enregistrement de clef "Voiture" et lui associé l'élément "Citroën".

Cette seconde méthode ne provoque pas d'erreur si la clef existe déjà.
 

Goufra

XLDnaute Occasionnel
Merci
Je me suis mal exprimé. J'avais compris votre démonstration ci-dessus.
mais dans le 1er code du site de j.b :

For Each c In plage
DsansDoublon(c.Value) = ""
Next c

je reviens à mes moutons... pour essayer de comprendre = ""
Je vous offre volontiers un café "virtuel" et toute ma reconnaissance, assurément.
Bien cordialement
JC Goufra
 

mapomme

XLDnaute Barbatruc
Supporter XLD
For Each c In plage
DsansDoublon(c.Value) = ""
Next c


Ce code permet de construire une liste de clefs uniques (au sein du dictionary).
Ce qui nous intéresse ici, c'est la liste des clefs au sein du dictionary et aucunement les valeurs associées aux clefs dont on se fiche royalement.
Donc on pourrait écrire DsansDoublon(c.Value) = "" ou DsansDoublon(c.Value) = 123456 ou DsansDoublon(c.Value) = "N'importe quoi" ou encore DsansDoublon(c.Value) = -1. Ce qui nous importe c'est la liste des clefs. Qu'on leur affecte un nombre, un texte ou la chaine de caractère vide, on n'en a cure. L'important c'est de mettre les clefs dans le dictionary.

Admettons que la plage comporte les valeurs (1, "toto", 456, "toto", 1)
  • Passage n°1 dans la boucle : on fait DsansDoublon(1)=-1. La clef n'existe pas dans dictionary, elle est donc créée avec comme item associé -1. Le dictionary contient donc l'ensemble (clef, item) égal à (1, -1)
  • Passage n°2 dans la boucle : on fait DsansDoublon("toto")=-1.La clef n'existe pas dans dictionary, elle est donc créée avec comme item associé -1. Le dictionary contient donc les ensembles (clef, item) égal à (1, -1) et ("toto",-1)
  • Passage n°3 dans la boucle : on fait DsansDoublon(456)=-1.La clef n'existe pas dans dictionary, elle est donc créée avec comme item associé -1. Le dictionary contient donc les ensembles (clef, item) égal à (1, -1) et ("toto",-1) et (456,-1)
  • Passage n°4 dans la boucle : on fait DsansDoublon("toto")=-1. La clef "Toto" existe déjà donc le dictionary ne la crée pas mais va remplacer l'item existant pour la clef "toto" par le nouvel item -1 (qui est était déjà -1 mais ça on s'en fiche!). Le dictionary contient donc les ensembles (clef, item) égal à (1, -1) et ("toto",-1) et (456,-1). Rien n'a changé.
  • Passage n°5 dans la boucle : on fait DsansDoublon(1)=-1. La clef 1 existe déjà donc le dictionary ne la crée pas mais va remplacer l'item existant pour la clef 1 par le nouvel item -1 (qui est était déjà -1 mais ça on s'en fiche! encore une fois). Le dictionary contient donc les ensembles (clef, item) égal à (1, -1) et ("toto",-1) et (456,-1). Rien n'a changé.

Donc à la fin de la boucle, le dictionnaire contient les ensembles (clef, item) égaux à (1, -1) et ("toto",-1) et (456,-1).
la liste des clefs est (1, "toto", 456) et la liste des items associés est(-1, -1, -1).
On trouve donc bien 3 valeurs uniques (DsansDoublon.count) et la liste de ces valeurs uniques est dans DsansDoublon.Keys.

On peut remplacer dans la boucle la valeur -1 par "". On serait arrivé au même résultat pour DsansDoublon.Keys.
 
Dernière édition:

Goufra

XLDnaute Occasionnel
Merci
Suis-je bête !
Ce qui me trouble:
j'obtiens un dictionnaire sans avoir eu besoin de mettre " if not Dsansdoublon exists"
Parfois je suis obligé de mentionner en début de code on error resume next, pour continuer.
Je suis vraiment navré de mon niveau d'incompréhension
Merci CP4
Bien cordialement
 

Discussions similaires