Objet dictionary (Dictionnaire Excel)

Accueil


Accés aux éléments d'un dictionnaire
Transfert d'un dictionnaire dans le tableur
Utilisation de Microsoft Scripting Runtime
Dictionnaire de dictionnaire
Liste triée d'un dictionnaire
Tri d'un dictionnaire
Dictionnaire comme index tableau 2D
Tableau comme élément d'un dictionnaire(dictionnaire muti-colonnes)
Dictionnaire multi-colonnes pour remplacer un Array 2D
Dictionnaire indicé
Extraction d'une partie d'un dictionnaire
Liste sans doublons
Liste des doublons
Liste des non doublons(valeurs uniques)
Liste sans doublons pour CombBox/ListBox
Liste sans doublons triée pour ComboBox/ListBox
Liste sans doublons 2 colonnes pour Combobox
Compter les éléments
Totalisation par groupe
Maj Stock
Sous total de tableau 2D avec indexation dictionnaire
Consolidation de plusieurs tableaux
Consolidation de tableaux 2D
Regroupement de lignes avec sous-total avec indexation tableau par dictionnaire
Fusion de lignes doublons
Regroupement dans une cellule
Statistiques 2D
Fonction de consolidation multi-zones
Transforme BD en tableau
Transforme Tableau en BD
Comparaison dictionary/collection
Comparaison Dictionary/Tableau/Find
Simulation Dictionary pour Excel Mac
Repérage de doublons
Fonction liste sans doublons triée
Rechv() perso plus rapide que Recherchev() & Sommeprod()
Alimenter un comboBox avec une liste triée sans doublons
Choix successifs(listes différences)
Fonction de comptage sans doublons avec critère
Suppression doublons dans une BD
Liste des doublons
Eléments communs à 2 listes
Liste Abréviations sans doublons
Liste des items sans doublons
Regroupement des items de chaque code
Liste des valeurs uniques de chaque code
Listes inverses
Comparaison de 2 classeurs
Fonction liste sans doublons triée multi-zones
Suppression de doublons multi-feuilles
Maj d'une liste existante
Indexation d'un tableau 2D par un dictionnaire
Meilleure note
Recherche d'une valeur proche
Synthèses 3D
Remplacer par multiple
Recherche rapide de mots dans une phrase
Indexation d'une BD pour recherche rapide de mots
Fonction frequence Texte
Nombre d'occurences des doublons
Communs à 3 listes
Extraction d'un Array
Fonction perso NBSIMAT plus rapide que NB.SI()
Nombre de valeurs uniques avec 1 ou 2 critères
Compte noms champ
Rapport
Dictionary pour remplacer sommprod
Maj colonne avec Dictionary au lieu de VLookUp


 

-L'objet Dictionary associe des valeurs à des clefs. L'objet Dictionary permet notamment de générer des listes sans doublons. Cet objet, simple à programmer, est très performant. Ne pas utiliser l'objet Collection qui est lent.
-Dictionary peut être vu comme un tableau à une dimension. On accède aux éléments par une clé et non pas un indice.
-Pour les ajouts/suppressions, l'objet Dictionary est + facile à utiliser qu'un tableau (Redim + indice à gérer).

L'accès aux clés particulièrement rapide doit s'expliquer par l'utilisation de hash-code qui fait correspondre à une clé alphabétique une adresse de rangement numérique - par un algorithme - et donc de la retrouver directement au lieu de balayer une table séquentiellement.

 

Add clé,élément

Ajoute une clé et la valeur associée

Exists(clé)

Teste l'existence d'une clé

Tbl=Items

Donne dans un tableau les éléments

Tbl=Keys

Donne dans un tableau les clés

Remove (clé)

Suprime la clé

Removeall

Supprime tous les éléments

Count

Donne le nombre d'éléments

Item(clé) =valeur

Modifie la valeur de la clé

Item(clé)

Donne la valeur associée à la clé

CompareMode=vbTextCompare

Ignore la casse

Accès aux éléments d'un dictionnaire

DictionaryAccés

Sub ListeDictionnaire()
  Set d = CreateObject("Scripting.Dictionary")
  d.Item("Dupont") = 35      ' ou If Not d.Exists("aa") Then d.Add "Dupont", 35
  d.Item("Durand") = 40
  d.Item("Martin") = 27       ' ou d("Durand")=40
  d.Item("Espinasse") = 32
  '---- élément pour une clé
  clé = "Durand"
  MsgBox clé & ":" & d.Item(clé)   ' ou MsgBox d(clé)
  '--- toutes les clés et valeurs associées
  For Each c In d.keys
     MsgBox c & ":" & d.Item(c) ' ou MsgBox c & ":" & d(c)
  Next c
  '---- 3eme élément
  a = d.keys     ' dans un tableau a(0 To d.Count-1)
  b = d.items    ' dans un tableau b(0 To d.Count-1)
  MsgBox a(2) & ":" & b(2)
  '--- Rang d'une clé
  clé = "Durand"
  p = Application.Match(clé, d.keys, 0)
  MsgBox "position de " & clé & ":" & p
  '--- Stats
  MsgBox "Total:" & Application.Sum(d.items)
  MsgBox "Mini:" & Application.Min(d.items)
  MsgBox "Moyenne:" & Application.Average(d.items)
End Sub

Pour que majuscules/minuscules soient confondues.

d.CompareMode = vbTextCompare

Transfert d'un dictionnaire dans le tableur

TransfertTableurTableaux

Sub TransfertDictionnaireTableur()
  Set d = CreateObject("Scripting.Dictionary")
  d.Item("Dupont") = 35
  d.Item("Durand") = 27
  d.Item("Martin") = 40
  d.Item("Espinasse") = 32
  '-- horizontal
  [E2].Resize(, d.Count) = d.keys
  [E3].Resize(, d.Count) = d.items
  '-- vertical 
  [A2].Resize(d.Count) = Application.Transpose(d.keys)
  [B2].Resize(d.Count) = Application.Transpose(d.items)
End Sub

Transfert d'un dictionnaire dans des tableaux

Le transfert des clés d'un dictionnaire dans une table a() se fait avec a=d.keys (Lbound(a) --> 0)

Sub TransfertDictionnaireTableaux()
  Set d = CreateObject("Scripting.Dictionary")
  d.Item("Dupont") = 35
  d.Item("Durand") = 27
  d.Item("Martin") = 40
  d.Item("Espinasse") = 32
  '--- Tableaux
  a = d.keys   ' transfert dans tableau a(0 To n-1)
  b = d.items  ' transfert dans tableau b(0 To n-1)
  [A2].Resize(d.Count) = Application.Transpose(a) ' Transfert des tableaux dans le tableur
  [B2].Resize(d.Count) = Application.Transpose(b)
End Sub

Utilisation de Microsoft scripting runtime dans Outils/Référence

Si Microsoft Scripting Runtime est coché dans Outils/Référence, on peut déclarer un dictionnaire par

Dim d As New Dictionary

Dans ce cas, on peut accéder aux clés et aux items par un indice.
L'intérêt des dictionnaires est l'accès par clé. Utiliser l'accès par un indice n'apporte rien par rapport aux Arrays().

'Microsoft scripting runtime doit être coché dans Outils/Référence
Dim d As New Dictionary
d.Item("Dupont") = 35 ' ou If Not d.Exists("aa") Then d.Add "Dupont", 35
d.Item("Durand") = 40
d.Item("Martin") = 27 ' ou d("Durand")=40
d.Item("Espinasse") = 32
MsgBox d.Keys(2)
MsgBox d.Items(2)

Dictionnaire de dictionnaire

Sub DictionnaireDictionnaire()
  'Microsoft scripting runtime est coché
  Dim d1 As New Scripting.Dictionary
  Dim d2 As New Scripting.Dictionary
  Dim dd As New Scripting.Dictionary ' dictionnaire de dictionnaire
  Dim Ptr As New Scripting.Dictionary

  d1.Add "aa", 11: d1.Add "bb", 22
  d2.Add "cc", 33: d2.Add "dd", 44

  Set dd("dico1") = d1: Set dd("dico2") = d2

  Set Ptr = dd("dico1")
  MsgBox Ptr.Items(1): MsgBox Ptr("bb") ' affiche 22
  Set Ptr = dd("dico2")
  MsgBox Ptr.Items(1): MsgBox Ptr("dd") ' affiche 44
End Sub

Tableau de dictionnaires

Tableau de dictionnaires

Problème d'inversion jour/mois pour les dates

Pour éviter l'inversion des jours/mois dans les dates, utiliser Value2

Set d = CreateObject("scripting.dictionary")
For Each c In [A2:a13]
  d(c.Value) = c.Offset(, 1).Value2
Next c
[d2].Resize(d.Count) = Application.Transpose(d.keys)
[E2].Resize(d.Count) = Application.Transpose(d.items)

Ou à la restitution, utiliser FormulaLocal

[E2].Resize(d.Count).FormulaLocal = Application.Transpose(d.items)

Accès à un item de dictionnaire par un indice

Les dictionnaires ont étés conçus pour un accès à un item par une clé.
L'accès par un indice se fait par la création explicite ou implicite d'un Array()

AccesIndice

Sub AccesItemParUnIndice()
  Set d = CreateObject("Scripting.Dictionary")
  d.Item("Dupont") = 35
  d.Item("Durand") = 27
  d.Item("Martin") = 40
  d.Item("Espinasse") = 32
  '---
  Tbl1 = d.keys 'tableau Tbl1() crée une seule fois
  Tbl2 = d.items 'tableau Tbl2() crée une seule fois
  For n = 0 To d.Count - 1
     MsgBox Tbl1(n) & ", " & Tbl2(n)
  Next n
  '--les tableaux Tbl1() et Tbl2() sont recrées implicitement à chaque Msgbox donc moins     performant
  For n = 0 To d.Count - 1
    MsgBox d.keys()(n) & " - " & d.items()(n)
  Next n
End Sub

Transfert d'un dictionnaire dans un autre dictionnaire

Sub TransfertDictionnaireDictionnaire()
  Set d1 = CreateObject("Scripting.Dictionary")
  d1.Item("Dupont") = 35
  d1.Item("Durand") = 27
  d1.Item("Martin") = 40
  d1.Item("Espinasse") = 32
  Set d2 = d1
  '-- horizontal
  [E2].Resize(, d2.Count) = d2.keys
  [E3].Resize(, d2.Count) = d2.items
End Sub

Liste triée d'un dictionnaire

Pour obtenir une liste triée d'un dictionnaire, on transfère le dictionnaire dans un tableau temp(n,2) que l'on tri.

Liste triée d'un dictionnaire

Keys

Items

Dupont

35

Durand

40

Espinasse

32

Martin

27

Sub ListeTriéeDictionnaire()
  Set d = CreateObject("Scripting.Dictionary")
  d.Item("Dupont") = 35
  d.Item("Martin") = 27
  d.Item("Durand") = 40
  d.Item("Espinasse") = 32
  Dim temp(): ReDim temp(1 To d.Count, 1 To 2)
  i = 1
  For Each c In d.keys
     temp(i, 1) = c
     temp(i, 2) = d(c)
     i = i + 1
   Next c
   Call Quick(temp, LBound(temp), UBound(temp))
  [A2].Resize(d.Count, 2).Value2 = temp
End Sub

Tri d'un dictionnaire

Pour trier un dictionnaire dans l'ordre des clés, il faut le transférer dans un Array, trier l'Array puis recréer le dictionnaire avec l'Array trié.

Tri Dico Keys sans items
Tri Dico Keys/Items Procédure
Tri Dico Keys/Items Fonction

Option Compare Text
Sub TriDico()
  Set f = Sheets("BD")
  Set d1 = CreateObject("Scripting.Dictionary")
  d1.CompareMode = vbTextCompare

  a = f.Range("A2:A" & f.[A65000].End(xlUp).Row) ' tableau a(n,1) pour rapidité
  For i = LBound(a) To UBound(a)
    If a(i, 1) <> "" Then d1(a(i, 1)) = ""
  Next i
  DicoTri d1
  f.[C2].Resize(d1.Count) = Application.Transpose(d1.keys)
End Sub

Sub DicoTri(dico)
  Tbl = dico.keys                           ' Transfert Dictionnaire dans Array
  Tri Tbl, LBound(Tbl), UBound(Tbl) ' Tri Array
  dico.RemoveAll                           ' Création du dictionnaire
  For i = LBound(Tbl) To UBound(Tbl)
    dico(Tbl(i)) = ""
  Next i
End Sub

Extraction d'une partie de dictionnaire

Dans l'exemple ci dessous, nous obtenons 30 élements d'un dictionnaire à partir du 30e

Sub ExtraitDico()
  Set d = CreateObject("scripting.dictionary")
  For i = 1 To 100: d(i) = "": Next
  '--- Extrait dico
  Position = 30
  taille = 20
  [A1].Resize(taille) = Application.Index(d.keys, Evaluate("Row(" & Position & ":" & Position + taille & ")"))
  'b= Application.Index(d.keys, Evaluate("Row(" & Position & ":" & Position + taille & ")"))
End Sub

Ci dessous, nous découpons un dictionnaire par tranches de 3

Sub decoupeDico()
  Set d = CreateObject("scripting.dictionary")
  For i = 1 To 12: d(i) = "": Next
  '--- découpe
  pas = 3
  For k = 0 To d.Count / pas - 1
    decal = k * pas + 1
   [C1].Resize(pas).Offset(k * (pas + 1)) = Application.Index(d.keys, Evaluate("Row(" & decal & ":" & decal + pas & ")"))
  Next k
End Sub

Liste sans doublons pour combobox ou listbox

FormLSD
FormLSDCollectionMAC
FormLSDCollectionTriéMAC
Fonction Sans Doublons
Fonction Sans Doublons Array
Fonction Sans Doublons triés
Fonction Sans Doublons triés MAC
Fonction Sans Doublons Trié Multi zones
Fonction Sans Doublons Trié Multi zones 3

Private Sub UserForm_Initialize()
  Set f = Sheets("BD")
  Set MonDico = CreateObject("Scripting.Dictionary")
  a = f.Range("A2:A" & f.[A65000].End(xlUp).Row)     ' tableau a(n,1) pour rapidité
  For i = LBound(a) To UBound(a)
     If a(i, 1) <> "" Then MonDico(a(i, 1)) = ""
  Next i
  Me.ComboBox1.List = MonDico.keys
End Sub

Liste sans doublons triée pour ComboBox ou ListBox

Form ComboBox trié

Private Sub UserForm_Initialize()
  Set f = Sheets("BD")
  Set mondico = CreateObject("Scripting.Dictionary")
  a = f.Range("A2:A" & f.[A65000].End(xlUp).Row) ' tableau a(n,1) pour rapidité
  For i = LBound(a) To UBound(a)
    If a(i, 1) <> "" Then mondico(a(i, 1)) = ""
  Next i
  '--avec tri
  temp = mondico.keys
  Call Tri(temp, LBound(temp), UBound(temp))
  Me.ComboBox1.List = temp

Sur MAC, Dictionary n'existe pas. Pour obtenir une liste sans doublons, utiliser Collection:

Form LSD Collection MAC
Form LSD Collection MAC 2
FormLSDTriéMAC

Option Compare Text
Dim f
Private Sub UserForm_Initialize()
  Set f = Sheets("BD")
  Dim a()
  a = Application.Transpose(f.Range("A2:A" & f.[A65000].End(xlUp).Row).Value)
  Me.ComboBox1.List = SansDoublonsMAC(a())
End Sub

Function SansDoublonsMAC(a())
  Dim Maliste As New Collection
  On Error Resume Next
  For i = LBound(a) To UBound(a)
     Maliste.Add Item:=a(i), key:=a(i)
  Next i
  On Error GoTo 0
  Dim b(): ReDim b(1 To Maliste.Count)
  For i = 1 To Maliste.Count
    b(i) = Maliste(i)
  Next i
  SansDoublonsMAC = Application.Transpose(b)
End Function

Elimine les doublons à l'intérieur d'une cellule

Sans Doublons Cellule
Sans Doublons Cellule MAC

Function SansDoublon(c, sep)
  a = Split(Application.Trim(c), sep)
  Set mondico = CreateObject("Scripting.Dictionary")
  For i = 0 To UBound(a): mondico.Item(a(i)) = 1: Next i
  SansDoublon = Join(mondico.keys, sep)
End Function

Listes sans doublons avec 2 colonnes (Nom+prénom)

Form Cascade SansDoublons 2 colonnes Dict
Form Sans Doublons plusieurs colonnes
FormCascadeSansDoublons2colonnesListBoxDict
Form Cascade Sans Doublons 2 colonnes Trié
Form Cascade Sans Doublons 2 colonnes Disjointes Trié
FormCascadeSansDoublons2colonnesMAC
Fonction Sans Doublons 2 colonnes

Dim f, a()
Private Sub UserForm_Initialize()
  Set f = Sheets("bd")
  a = f.Range("A2:D" & f.[B65000].End(xlUp).Row).Value
  Set d = CreateObject("Scripting.Dictionary")
  j = 0
  For i = LBound(a) To UBound(a)
    tmp = a(i, 1) & a(i, 2)
    If Not d.exists(tmp) Then
      d(tmp) = ""
      Me.ComboBox1.AddItem a(i, 1)
      Me.ComboBox1.List(j, 1) = a(i, 2)
      j = j + 1
    End If
  Next i
End Sub

ou

Dim f, a()
Private Sub UserForm_Initialize()
  Set f = Sheets("bd")
  a = f.Range("A2:D" & f.[B65000].End(xlUp).Row).Value
  Set d = CreateObject("Scripting.Dictionary")
  n = 0
  Dim b()
  For i = LBound(a) To UBound(a)
    tmp = a(i, 1) & a(i, 2)
    If Not d.exists(tmp) Then
      d(tmp) = ""
      n = n + 1
      ReDim Preserve b(1 To 2, 1 To n)
      b(1, n) = a(i, 1): b(2, n) = a(i, 2)
     End If
   Next i
   Me.ComboBox1.Column = b
End Sub

ou

Dim f, a()
Private Sub UserForm_Initialize()
  Set f = Sheets("bd")
  a = f.Range("A2:D" & f.[B65000].End(xlUp).Row).Value
  Me.ComboBox1.List = f.Range("A2:D" & f.[B65000].End(xlUp).Row).Value
  Set d = CreateObject("Scripting.Dictionary")
  j = 0
  Do While j < Me.ComboBox1.ListCount
    tmp = ComboBox1.List(j, 0) & ListBox1.List(j, 1)
    If Not d.exists(tmp) Then
     d(tmp) = ""
     j = j + 1
   Else
      Me.ComboBox1.RemoveItem j
   End If
Loop
End Sub

Utilisation de Sum,Average,Match,Max,Min avec le dictionnaire

ttal = Application.Sum(d.items) donne la somme des items
Moy = Application.Average(d.items) donne la moyenne
p=Application.Match("toto",d.keys,0) donne la postion de toto dans le dictionnaire

Dictionnaire comme index de tableau (Array)

Pour retrouver plus rapidement la ligne d'un tableau Tbl(,) corrrespondant à un nom, on peut indexer le tableau Tbl(,) avec un dictionnaire.
Remarque :Le système d'indexation d'un tableau 2D par un dictionnaire est plus rapide qu'un dictionnaire multi-colonnes.

IndexDico

Sub essaiIndex()
  Set d = CreateObject("Scripting.Dictionary")
  Tbl = [A2:D6] ' BD
  '----- création du dictionnaire index
  For i = 1 To UBound(Tbl)
     d(Tbl(i, 1)) = i
  Next i
  '------ recherche de Durand
  clé = "Durand"
  ligne = d(clé)
  [G2:J2] = Application.Index(Tbl, ligne)
End Sub

Dictionnaire multi-colonnes (tableau comme élément de dictionnaire)

Les items d'un dictionnaire peuvent être des tableaux.

DicoTab
Dico structure

Clé

 

Ville

Salaire

Age

Martin

->

Lyon

5000

23

Dupont

->

Paris

5000

35

Durand

->

Paris

4000

22

 

 

 

 

 

Sub DictionnaireArray()
  Set d = CreateObject("Scripting.Dictionary")
  ville = "Paris"
  d.Item("Martin") = Array(ville, 5000, 24)
  d.Item("Toto") = Array("Lyon", 6000, 25)
  d.Item("Titi") = Array("Issy", 6000, 34)
  [A2].Resize(d.Count) = Application.Transpose(d.keys)
  MsgBox d.Item("Toto")(0)
  b = Application.Transpose(Application.Transpose(d.items)) ' dictionnaire dans array b(1 to n,1 to 3)
  [B2].Resize(UBound(b), UBound(b, 2)) = b
  [A1:D1] = Array("Nom", "Ville", "Salaire", "Age")
End Sub

Autres exemples

Sub EssaiDictionnaire()
  Set d = CreateObject("Scripting.Dictionary")
  ville = "Lyon"
  d.Item("Martin") = Array(ville, 5000, 23)
  MsgBox d.Item("Martin")(0)
  '--
  Dim a(1 To 3)
  a(1) = "Paris": a(2) = 5000: a(3) = 45 ' tableau a()
  d.Item("Dupont") = a
  MsgBox d.Item("Dupont")(1)
  b = d.Item("Dupont")
  MsgBox b(3)
  For Each c In d.Item("Dupont")
     MsgBox c
  Next c
  [N1].Resize(, 3) = d.Item("Dupont")   ' affiche la fiche de Dupont
End Sub

Sub Array2DdansDictionnaire()
  Set d = CreateObject("Scripting.Dictionary")
  '--- transfert Array dans dico
  a = [A2:D4]
  Ncol = UBound(a, 2)
  ReDim tmp(1 To Ncol)
  For i = LBound(a) To UBound(a)
    For k = 1 To Ncol: tmp(k) = a(i, k): Next
    d(a(i, 1)) = tmp ' ou d(a(i, 1)) = Application.Index(a, i)
  Next i
  'MsgBox d.Item("Toto")(2)
  '------------ récup dico dans Array 2D
  b = Application.Transpose(Application.Transpose(d.items)) ' dictionnaire dans array b(1 to n,1 to 4)
  [G2].Resize(UBound(b), UBound(b, 2)) = b
End Sub

Autre exemple

Sub Essai()
  Set d = CreateObject("Scripting.Dictionary")
  a = Evaluate("{1,2,3;4,5,6;7,8,9;10,11,12}")   ' 1 à 4 x 1 à 3
  d.Item("xx") = a
  Z = d("xx")(2, 1)                          ' a(2,1)
  MsgBox Z
  b = Application.Index(d("xx"), 2)   ' 2e ligne
  MsgBox b(2)
  c = Application.Index(d("xx"), , 3) ' 3e colonne
  MsgBox c(2, 1)
End Sub

On veut la première ligne de chaque équipe

Sans Doublons Multi-colonnes

Sub SansDoublons()
  Set d = CreateObject("Scripting.Dictionary")
  a = [A2:D9]
  For i = LBound(a) To UBound(a)
    If Not d.exists(a(i, 4)) Then
      d.Item(a(i, 4)) = Array(a(i, 1), a(i, 2), a(i, 3), a(i, 4))
    End If
  Next i
  [g2].Resize(d.Count, UBound(a, 2)) = Application.Transpose(Application.Transpose(d.items))
End Sub

ou

Sub SansDoublons2()
  Set d = CreateObject("Scripting.Dictionary")
  a = [A2:D9]
  ReDim b(1 To UBound(a, 2))
  For i = LBound(a) To UBound(a)
    If Not d.exists(a(i, 4)) Then
      For k = 1 To UBound(a, 2): b(k) = a(i, k): Next
      d.Item(a(i, 4)) = b
    End If
  Next i
  [g2].Resize(d.Count, UBound(a, 2)) = Application.Transpose(Application.Transpose(d.items))
End Sub

Dictionnaire indicé

Sur cet exemple, nous extrayons les éléments distincts de chaque colonne d'une bd ainsi que le nombre d'apparitions de chque élément.

Sans doublons colonnes BD

Sub DicoIndice()
  Set f = Sheets("bd")
  Tbl = f.[A1].CurrentRegion.Value
  NbCol = UBound(Tbl, 2)
  NbLig = UBound(Tbl)
  Dim d(): ReDim d(1 To NbCol)
  Dim Titre(): ReDim Titre(1 To NbCol)
  For col = 1 To NbCol
    Set d(col) = CreateObject("scripting.dictionary")
    d(col).CompareMode = vbTextCompare ' ignore la casse
  Next col
  For col = 1 To NbCol ' remplissage dictionnaire
     Titre(col) = Tbl(1, col)
     For lig = 2 To NbLig
        tmp = Tbl(lig, col)
        d(col)(tmp) = d(col)(tmp) + 1
     Next lig
  Next col
  Set f2 = Sheets("result") ' affichage résultat
  For col = 1 To NbCol
    Set Rng = f2.Cells(2, (col - 1) * 2 + 1).Resize(d(col).Count)
    f2.[A1].Offset(, (col - 1) * 2) = Titre(col)
    Rng.Value = Application.Transpose(d(col).keys)
    Rng.Offset(, 1) = Application.Transpose(d(col).items)
    Rng.Resize(, 2).Sort key1:=Rng(1).Offset(, 1), Order1:=xlDescending
  Next col
  f2.[A1].CurrentRegion.Columns.AutoFit
  f2.Select
End Sub

Simulation Array Indicés

Simulation Array Indicés

Sous total tableau multicolonnes

Ici, nous effectuons un sous total d'un tableau multi-colonnes dans un dictionnaire multi-colonnes.

Sous total multi-colonnes
Fonction Suppression des doublons d'un Array
Suppression des doublons d'un Array (toutes colonnes)

Dim d1
Sub Stat()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set f1 = Sheets("données")
  Ncol = f1.[a1].CurrentRegion.Columns.Count
  a = f1.[a1].CurrentRegion
  Totalise a
  Set f2 = Sheets("résultats")
  f1.[a1].Resize(, Ncol).Copy f2.[a1]
  f2.[a2].Resize(d1.Count, 1) = Application.Transpose(d1.keys)
  f2.[B2].Resize(d1.Count, Ncol - 1) = Application.Transpose(Application.Transpose(d1.items))
  f2.Activate
  '[a1].CurrentRegion.Sort Key1:=Range("a2"), Header:=xlYes
End Sub

Sub Totalise(a)
  ReDim Titem(1 To UBound(a, 2)) ' table des items d'une clé
  For ligne = 2 To UBound(a)
    crit = a(ligne, 1)
    If Not d1.exists(crit) Then For k = 1 To UBound(a, 2): Titem(k) = 0: Next k: d1(crit) = Titem
    For k = 1 To UBound(a, 2): Titem(k) = d1.Item(crit)(k): Next k
    For col = 2 To UBound(a, 2)
      If a(ligne, col) <> "" Then Titem(col - 1) = Titem(col - 1) + Val(a(ligne, col))
    Next col
    d1.Item(crit) = Titem
  Next ligne
End Sub

Tri d'un dictionnaire multi-colonnes

Liste triée d'un dictionnaire multi-colonnes

Sub ListeTriéeDictionnaireMultiCol()
  Set d = CreateObject("Scripting.Dictionary")
  d.Item("Dupont") = Array("Paris", 5000, #12/10/1980#)
  d.Item("Martin") = Array("Lyon", 4000, #12/10/1980#)
  d.Item("Durand") = Array("Issy", 6000, #12/10/1980#)
  d.Item("Espinasse") = Array("Montigny", 3000, #12/10/1990#)
  Dim temp(): ReDim temp(1 To d.Count, 1 To 2)
  i = 1
  For Each c In d.keys
    temp(i, 1) = c
    temp(i, 2) = d(c)
    i = i + 1
   Next c
   Call Quick(temp, LBound(temp), UBound(temp))
   For i = LBound(temp) To UBound(temp)
     Cells(i + 1, 1) = temp(i, 1)
     Cells(i + 1, 2) = temp(i, 2)(0)
     Cells(i + 1, 3) = temp(i, 2)(1)
   Next i
End Sub

Private Sub Quick(a, gauc, droi) ' Quick sort
  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
       temp = a(g, 1): a(g, 1) = a(d, 1): a(d, 1) = temp
       temp = a(g, 2): a(g, 2) = a(d, 2): a(d, 2) = temp
       g = g + 1: d = d - 1
     End If
   Loop While g <= d
   If g < droi Then Call Quick(a, g, droi)
   If gauc < d Then Call Quick(a, gauc, d)
End Sub

ClasseBD Dictionary

Dictionnaire multi-colonnes

En remplaçant un Array 2D par un dictionnaire multi-colonnes:

-Les suppressions de lignes ou ajouts deviennent très simples
-Le dictionnaire multi-colonnes peut être transféré dans Array 2D classique b(,)

Encapsule Array
TransfertBD sans lignes vides dans ListBox
ListBox conditionnelle
TransfertBD sans lignes vides dans ListBox Trié
RegroupeSousTotal Plusieurs Colonnes Plusieurs champs 2
RegroupeSousTotal Plusieurs Colonnes Plusieurs Champs 3 Dico

Sub ArrayEncapsuléDico()
  Set d = CreateObject("Scripting.Dictionary")
  a = [A2:C6]
  For i = LBound(a) To UBound(a)
     d.Item(a(i, 1)) = Array(a(i, 1), a(i, 2), a(i, 3))
  Next i
  '--accès à la ville de Durand
  [k2] = d("Durand")(1)
  '---suppression d'une ligne
  d.Remove ("Espinasse")
  '----- affichage du tableau dans le tableur
  b = Application.Transpose(Application.Transpose(d.items)) ' dictionnaire dans array b(1 to n,1 to 3)
  [F2].Resize(UBound(b), UBound(b, 2)) = b
  'MsgBox b(1, 2) ' accès à un item de b(,)
End Sub

Liste sans doublons

ListeSansDoublons
Liste sans doublons Array MAC
Liste sans doublons Fonction

0,26 sec pour 16.000 lignes

Sub ListeSansDoublons()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    mondico(c.Value) = ""
  Next c
  [C2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
End Sub

Remarque
mondico(c.Value) = "" <=> mondico.Item(c.Value) = ""

Avec le transfert du champ dans un tableau intermédiaire, la création du dictionnaire est + rapide(0,04 sec pour 16.000 lignes au lieu de 0,26 sec)

Sub ListeSansDoublons()
  Set mondico = CreateObject("Scripting.Dictionary")
  a = Range("a2:a" & [a65000].End(xlUp).Row)   ' tableau a(n,1) 
  For i = LBound(a) To UBound(a)
    mondico(a(i, 1)) = ""
  Next i
  [c2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
End Sub

Si les noms sont écrits avec une casse différente

Sub ListeSansDoublons()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    mondico(UCase(c.Value)) = ""
  Next c
  [C2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
End Sub

Liste sans doublons triée

LSDTriée

Sub ListeSansDoublonsTriée()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
     mondico(c.Value) = ""
  Next c
  [C2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
  [C1].Sort Key1:=[C2], Order1:=xlAscending, Header:=xlYes
End Sub

Compter les éléments différents d'un champ

Pour 10.000 éléments, on obtient 0,015 sec avec le dictionnaire et 3 sec avec Evaluate

Compte éléments différents

Sub ElementsDifferentsDico()
  t = Timer()
  Set d1 = CreateObject("Scripting.Dictionary")
  Tbl = Range("B2:B" & [b65000].End(xlUp).Row)
  For i = 1 To UBound(Tbl)
    d1(Tbl(i, 1)) = ""
  Next i
  MsgBox Timer() - t
  MsgBox d1.Count
End Sub

Avec Evaluate

Sub ElementsDifferentsDicoEvaluate()
  t = Timer()
  Dim Nb As Long, derlig
  derlig = Cells(Rows.Count, 2).End(xlUp).Row
  Nb = Evaluate("SUMPRODUCT(1/COUNTIF(B2:B" & derlig & ",B2:B" & derlig & "))")
  MsgBox Timer() - t
  MsgBox Nb
End Sub

Cumul des montants sans les doublons

Cumul sans les doublons

Sub CumulMontantSansDoublons()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set f1 = Sheets("BD")
  Tbl = f1.[a1].CurrentRegion
  Ttal = 0
  For i = 2 To UBound(Tbl)
    If Not d1.exists(Tbl(i, 1)) Then Ttal = Ttal + Tbl(i, 2): d1(Tbl(i, 1)) = ""
  Next i
  MsgBox Ttal
End Sub

Compter le nombre d'éléments par code

Pour avoir la liste sans doublons et le nombre d'occurences de chaque item.

Compte éléments

Sub CompteItems()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    mondico(c.Value) = mondico(c.Value) + 1
  Next c
  [c2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
  [d2].Resize(mondico.Count, 1) = Application.Transpose(mondico.items)
  [C1].Sort Key1:=[C2], Order1:=xlAscending, Header:=xlYes
End Sub

Remarque
mondico(c.Value) = mondico(c.Value) + 1 <=>mondico.Item(c.Value) = mondico.Item(c.Value) + 1

Si la colonne A est modifiée, on peut transformer la macro en fonction perso matricielle pour obtenir une maj automatique.

Fonction Compte
Fonction Compte MAC

Function Compte(champ As Range)
  Application.Volatile
  Set d = CreateObject("Scripting.Dictionary")
  a = champ
  For i = LBound(a) To UBound(a)
     clé = a(i, 1)
     d(clé) = d(clé) + 1
   Next i
   Dim temp()
   ReDim temp(1 To Application.Caller.Rows.Count, 1 To 2)
   i = 1
   For Each c In d.keys
      temp(i, 1) = c
       temp(i, 2) = d(c)
       i = i + 1
   Next
   Compte = temp
End Function

Totalisation par groupe

Totalisation par groupe
Totalisation par groupe 2
Totalisation 2 critères 1 colonne

Sub Recap()
  Set f = Sheets("BD"): Set result = f.Range("D2")
  Set Dico = CreateObject("Scripting.Dictionary")
  TblBD = f.Range("A2:B" & f.[A65000].End(xlUp).Row)
  For i = 1 To UBound(TblBD): Dico(TblBD(i, 1)) = Dico(TblBD(i, 1)) + TblBD(i, 2): Next i
  result.CurrentRegion.Offset(1).ClearContents
  result.Resize(Dico.Count, 1) = Application.Transpose(Dico.Keys)
  result.Offset(, 1).Resize(Dico.Count, 1) = Application.Transpose(Dico.Items)
  result.CurrentRegion.Sort key1:=result.Offset(1, 1), Order1:=xlDescending, Header:=xlYes
  result.Offset(Dico.Count, 1) = Application.Sum(Dico.Items)
End Sub

Nombre d'occurences uniques

Items Différents

Function ItemsDifferents(champ)
  Application.Volatile
  Set d1 = CreateObject("Scripting.Dictionary")
  a = champ
  For i = 1 To champ.Count
    If a(i, 1) <> "" Then d1(a(i, 1)) = ""
  Next i
  ItemsDifferents = d1.Count
End Function

Sous total de tableau 1 colonne

Sous Totaux
Sous Totaux2
Fonction Sous Totaux
Regroupe
Merge 2 Tables

Sub SousTotal()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
     mondico(c.Value) = mondico(c.Value) + c.Offset(, 1).Value
  Next c
  [e2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
  [f2].Resize(mondico.Count, 1) = Application.Transpose(mondico.items)
  [E1].Sort Key1:=[E2], Order1:=xlAscending, Header:=xlYes
End Sub

Avec des fonctions standards (cf programme)

Sub TotalCA()
  tablo = [A2:B10]
  b = Somme(tablo, 1, 2)        ' Total des CA par ville
  TriTab b, 2, "D"                    ' Trié en ordre décroissant

  [e2].Resize(UBound(b), 2) = b
End Sub

Sous total de 2 colonnes avec tableaux et indexation par dictionnaire

a/Si le tableau TblE() n'est pas trié:

Sous Total 2 col Num
Sous Total 2 col Num MAC
Sous Total 2 col Num Total Colonnes
Sous Total 2 col Texte

La table TblS() est indexée par le dictionnaire d pour permettre un accès plus rapide à chaque ligne de la table TblS().

Sub SousTotalNonTrié()
  Set d = CreateObject("Scripting.Dictionary")
  TblE = Range("A2:C" & [a65000].End(xlUp).Row)                           ' Table entrée
  Dim TblS(): ReDim TblS(1 To UBound(TblE), 1 To UBound(TblE, 2)) ' Table sortie
  For i = LBound(TblE) To UBound(TblE)
    If d.exists(TblE(i, 1)) Then
      lig = d(TblE(i, 1))            ' Récupération index TblS()
   Else
     d(TblE(i, 1)) = d.Count + 1: lig = d.Count: TblS(lig, 1) = TblE(i, 1)
   End If
   For c = 2 To UBound(TblE, 2): TblS(lig, c) = TblS(lig, c) + TblE(i, c): Next c   ' Totalisation numérique
  Next i
[E2].Resize(d.Count, UBound(TblS, 2)) = TblS
End Sub

ou

Sub SousTotalNonTrié2()
  Set d = CreateObject("Scripting.Dictionary")
  TblE = Range("A2:C" & [a65000].End(xlUp).Row)                            ' Table entrée
  Dim TblS(): ReDim TblS(1 To UBound(TblE), 1 To UBound(TblE, 2))  ' Table sortie
  maxi = 0
  For i = LBound(TblE) To UBound(TblE)
    If d.exists(TblE(i, 1)) Then
       p = d(TblE(i, 1))                             ' Récupération index TblS()
       TblS(p, 2) = TblS(p, 2) + TblE(i, 2)
       TblS(p, 3) = TblS(p, 3) + TblE(i, 3)
    Else
       maxi = maxi + 1
       d(TblE(i, 1)) = maxi
       TblS(maxi, 1) = TblE(i, 1)
       TblS(maxi, 2) = TblE(i, 2)
       TblS(maxi, 3) = TblE(i, 3)
    End If
  Next i
  [E2].Resize(d.Count, UBound(TblS, 2)) = TblS
End Sub

b/Si la table TblE() est triée (fonctionne sur PC & MAC)

Cette méthode est aussi rapide que l'utilisation du dictionnaire. Elle peut être utilisée pour le MAC qui ne dispose pas du dictionnaire.
Pour 20.000 lignes (0,07 sec -0,03 sec si déjà trié/ Dictionary: 0,05 sec/ Collection: 0,11 sec)

Sous Total 2 col triée

Sub SousTotalTablo2col()
  [A1].CurrentRegion.Sort , key1:=[A2], Header:=xlYes
  TblE = Range("A2:D" & [a65500].End(xlUp).Row)                             ' Table entrée
  Dim TblS(): ReDim TblS(1 To UBound(TblE), 1 To UBound(TblE, 2))   ' Table sortie
  i = 1: n = 0
  Do While i <= UBound(TblE)
    n = n + 1
    clé = TblE(i, 1)
    For c = 1 To 2: TblS(n, c) = TblE(i, c): Next c
    Do While TblE(i, 1) = clé
       For c = 3 To 4: TblS(n, c) = TblS(n, c) + TblE(i, c): Next c
       i = i + 1: If i > UBound(TblE) Then Exit Do
    Loop
  Loop
  [J2].Resize(n, UBound(TblS, 2)) = TblS
End Sub

Avec l'objet Collection (fonctionne sur PC & MAC)

Sub SousTotalCollection()
  Dim Collec1 As New Collection
  TblE = Range("A2:D" & [a65000].End(xlUp).Row)                           ' Table entrée
  Dim TblS(): ReDim TblS(1 To UBound(TblE), 1 To UBound(TblE, 2)) " Table sortie
  For i = LBound(TblE) To UBound(TblE)
    clé = TblE(i, 1)
    On Error Resume Next
    Collec1.Add Item:=Collec1.Count + 1, Key:=clé
    If Err > 0 Then
       lig = Collec1(clé)
    Else
       lig = Collec1.Count: TblS(lig, 1) = TblE(i, 1): TblS(lig, 2) = TblE(i, 2)
    End If 
    For c = 3 To UBound(TblE, 2): TblS(lig, c) = TblS(lig, c) + TblE(i, c): Next c   ' Totalisation colonnes 3 & 4
  Next i
  [J2].Resize(n, UBound(TblS, 2)) = TblS
End Sub

Critère multi-colonnes

La clé de regroupement est la concaténation du nom et du prénom.

clé = TblE(i, 1) & "|" & TblE(i, 2)

Sous Total 2 col critère multi-colonnes

Sub SousTotalNonTrié()
  Set d = CreateObject("Scripting.Dictionary")
  TblE = Range("A2:D" & [a65000].End(xlUp).Row)                           ' Table entrée
  Dim TblS(): ReDim TblS(1 To UBound(TblE), 1 To UBound(TblE, 2)) ' Table sortie
  For i = LBound(TblE) To UBound(TblE)
    clé = TblE(i, 1) & "|" & TblE(i, 2)    ' Clé multi-colonnes
   If d.exists(clé) Then
     lig = d(clé)             ' Récupération index TblS()
   Else
     d(clé) = d.Count + 1: lig = d.Count: TblS(lig, 1) = TblE(i, 1): TblS(lig, 2) = TblE(i, 2)   ' Nouvelle clé
   End If
   For c = 3 To UBound(TblE, 2): TblS(lig, c) = TblS(lig, c) + TblE(i, c): Next c ' Totalisation colonnes numériques
  Next i
 [F2].Resize(d.Count, UBound(TblS, 2)) = TblS
End Sub

On peut obtenir obtenir ces résulats avec des fonctions persos (réutilisables) par des utilsateurs ne connaissant pas VBA.

Fonction sous-total 2 critères

Sous-total avec plusieurs colonnes

Synthèse tableau plusieurs colonnes
Synthèse tableau plusieurs colonnes2

Sub SousTotalPLusieursColonnes()
  Set f = Sheets("données")
  Set Result = f.Range("J1")      ' Adresse résultat
  TblBD = f.Range("A1:E" & f.[A65000].End(xlUp).Row) ' Array pour rapidité
  Ncol = UBound(TblBD, 2)
  Set d = CreateObject("Scripting.Dictionary")
  Dim TblS(): ReDim TblS(1 To UBound(TblBD), 1 To Ncol)
  Dim TblTotCol(): ReDim TblTotCol(1 To Ncol)
  Dim TblTotLig(): ReDim TblTotLig(1 To 1000)
  For i = 2 To UBound(TblBD)
    clé = TblBD(i, 1)
    If Not d.exists(clé) Then d(clé) = d.Count + 1: lig = d(clé) Else lig = d(clé) ' gestion index
      For k = 2 To Ncol
        TblS(lig, k) = TblS(lig, k) + TblBD(i, k)
        TblTotCol(k) = TblTotCol(k) + TblBD(i, k) ' total colonne
        TblTotLig(lig) = TblTotLig(lig) + TblBD(i, k) ' total ligne
      Next k
   Next i
   Result.Offset(1).Resize(d.Count + 1, Ncol) = TblS
   Result.Offset(1).Resize(d.Count, 1) = Application.Transpose(d.keys)         ' titres lignes
   Result.Offset(1, Ncol).Resize(d.Count) = Application.Transpose(TblTotLig) ' totaux lignes
   Result.Resize(1, Ncol) = Application.Index(TblBD, 1)    ' titres colonnes
   Result.Offset(d.Count + 1).Resize(, Ncol) = TblTotCol   ' totaux colonnes
   Result.Offset(1).Resize(d.Count, Ncol + 2).Sort key1:=Result.Offset(1), Order1:=xlAscending, Header:=xlNo
End Sub

Tableau avec plusieurs colonnes espacées.

Synthèse tableau plusieurs colonnes espacées

Autre exemple

Totalisation de la colonne 1 par PartNumber

La table T1() n'est pas triée par partNumber. La table T2() est indexée par le dictionnaire pour permettre un accès plus rapide à chaque ligne de la table T2().

Sous Total Tableau 2D
Sous Total Tableau 2D 2

Sub SousTotalNonTrié()
  Set d = CreateObject("Scripting.Dictionary")
  T1 = Range("A2:D" & [a65000].End(xlUp).Row)
  Dim T2(): ReDim T2(1 To UBound(T1), 1 To UBound(T1, 2))
  For i = LBound(T1) To UBound(T1)
    If d.exists(T1(i, 2)) Then p = d(T1(i, 2)) Else d(T1(i, 2)) = d.Count + 1: p = d.Count
    T2(p, 1) = T2(p, 1) + T1(i, 1) ' totalisation colonne Qte
    For k = 2 To 4: T2(p, k) = T1(i, k): Next k ' copie des autres colonnes
  Next i
  [H2].Resize(d.Count, UBound(T2, 2)) = T2
  [A1:D1].Copy [H1]
End Sub

Regroupement dans un Array() avec critère multi-colonnes (élimnination des doublons)

Regroupement

Sub Regroupement()
  Set d = CreateObject("Scripting.Dictionary")
  TblE = Range("A2:L13")                                          ' Array entrée
  Dim TblS(): ReDim TblS(1 To UBound(TblE), 1 To 4)  ' Array sortie
  For i = LBound(TblE) To UBound(TblE)
    clé = TblE(i, 3) & "|" & TblE(i, 4) ' clé de regroupement
    If Not d.exists(clé) Then
      d(clé) = "": lig = d.Count: TblS(lig, 1) = TblE(i, 3): TblS(lig, 2) = TblE(i, 4) ' Nouvelle clé
      For c = 3 To UBound(TblS, 2): TblS(lig, c) = TblE(i, c + 4): Next c   ' recopie des autres colonnes
    End If
  Next i
  [C17].Resize(d.Count, UBound(TblS, 2)) = TblS
End Sub

Consolidation de plusieurs tableaux

Consolide Tableaux

Dim d1, d2
Sub consolide2()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  a = [E3:G3].Resize([E2].CurrentRegion.Rows.Count - 2)
  totalise a
  a = [I3:K3].Resize([I2].CurrentRegion.Rows.Count - 2)
  totalise a
  [a3].Resize(d1.Count) = Application.Transpose(d1.keys)
  [b3].Resize(d1.Count) = Application.Transpose(d1.items)
  [c3].Resize(d2.Count) = Application.Transpose(d2.items)
End Sub

Sub totalise(a)
  For i = LBound(a) To UBound(a)
    d1(a(i, 1)) = d1(a(i, 1)) + a(i, 2)
    d2(a(i, 1)) = d2(a(i, 1)) + a(i, 3)
  Next i
End Sub

Consolidation de tableaux 2D

Consolide tableaux 2D

Option Compare Text
Dim d1, d2, TblE(), Tstat(), TblTotLig(), TblTotCol()
Sub ConsolideTab2D()
  Set d1 = CreateObject("Scripting.Dictionary") ' lignes
  d1.CompareMode = vbTextCompare
  Set d2 = CreateObject("Scripting.Dictionary") ' colonnes
  d2.CompareMode = vbTextCompare
  ReDim Tstat(1 To 100, 1 To 100): ReDim TblTotLig(1 To 100): ReDim TblTotCol(1 To 100)
  TblE = Sheets("BD1").Range("C1").CurrentRegion.Value
  Consolide
  TblE = Sheets("BD2").Range("C1").CurrentRegion.Value
  Consolide
  Set Result = Range("a1")
  Result.Offset(1, 1).Resize(d1.Count, d2.Count) = Tstat
  Result.Offset(1).Resize(d1.Count) = Application.Transpose(d1.keys)  ' titres lignes
  Result.Offset(, 1).Resize(, d2.Count) = d2.keys                                ' titres colonnes
  Result.Offset(1).Offset(d1.Count, 1).Resize(, d2.Count) = TblTotCol   ' totaux colonnes
  Result.Offset(1, 1).Offset(, d2.Count).Resize(d1.Count) = Application.Transpose(TblTotLig) ' totaux lignes
  Result.Offset(d1.Count + 1) = "Total "
  Result.Offset(, d2.Count + 1) = "Total"
  '---tri lignes et colonnes
  Set Rng = Result.CurrentRegion
    Rng.Offset(1).Resize(Rng.Rows.Count - 1, Rng.Columns.Count).Sort key1:=Rng.Cells(2, 1), _
  Order1:=xlAscending, Header:=xlNo, MatchCase:=False, Orientation:=xlSortColumns
  Rng.Offset(, 1).Resize(Rng.Rows.Count, Rng.Columns.Count - 1).Sort key1:=Rng.Cells(1, 2), _
    Order1:=xlAscending, Header:=xlNo, MatchCase:=False, Orientation:=xlSortRows
End Sub

Sub Consolide()
  For i = 2 To UBound(TblE)
  clé = TblE(i, 1) & " " & TblE(i, 2)
  If d1.exists(clé) Then lig = d1(clé) Else d1(clé) = d1.Count + 1: lig = d1.Count    ' gestion index
  For c = 3 To UBound(TblE, 2)
    If d2.exists(TblE(1, c)) Then col = d2(TblE(1, c)) Else d2(TblE(1, c)) = d2.Count + 1: col = d2.Count ' index
      Tstat(lig, col) = Tstat(lig, col) + TblE(i, c)
      TblTotLig(lig) = TblTotLig(lig) + TblE(i, c)
      TblTotCol(col) = TblTotCol(col) + TblE(i, c)
   Next c
  Next i
End Sub

Maj Stock

Nous mettons à jour le stock avec les ventes.

Maj stock
Saisie ventes de produits & maj stock1
Saisie ventes de produits & maj stock2
Saisie ventes de produits & maj stock3
Saisie ventes de produits & maj stock4
Saisie commande de produits & maj stock

Sub majstock()
  Set f = Sheets("stock") ' lecture stock dans dico
  Set d = CreateObject("scripting.dictionary")
  Set Rng = f.Range("A3:A" & f.[A65000].End(xlUp).Row)
  For Each c In Rng
     If c.Value <> "" Then d(c.Value) = c.Offset(, 1)
  Next c
  '---- soustraction des ventes du stock
  Set Rng2 = f.Range("D3:D" & f.[D65000].End(xlUp).Row)
  For Each c In Rng2
    If c.Value <> "" Then d(c.Value) = d(c.Value) - c.Offset(, 1)
  Next c
  f.[A3].Resize(d.Count) = Application.Transpose(d.keys)
  f.[B3].Resize(d.Count) = Application.Transpose(d.items)
End Sub

Fusion de 2 tableaux

Fusion_2014_2015
Fusion_2014_2015_2

Sub fusion()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set f1 = Sheets("ca2014")
  a = f1.Range("A2:B" & f1.[a65000].End(xlUp).Row)
  Set f2 = Sheets("ca2015")
  b = f2.Range("A2:B" & f2.[a65000].End(xlUp).Row)
  n = UBound(a) + UBound(b)
  Dim c: ReDim c(1 To n, 1 To 3)
  m = 0
  For i = LBound(a) To UBound(a)
    If Not d1.exists(a(i, 1)) Then m = m + 1: d1(a(i, 1)) = m: p = m Else p = d1(a(i, 1))
    c(p, 1) = a(i, 1): c(p, 2) = a(i, 2)
  Next i
  For i = LBound(b) To UBound(b)
    If Not d1.exists(b(i, 1)) Then m = m + 1: d1(b(i, 1)) = m: p = m Else p = d1(b(i, 1))
    c(p, 1) = b(i, 1): c(p, 3) = b(i, 2)
  Next i
  Sheets("fusion").[A2].Resize(d1.Count, UBound(c, 2)) = c
End Sub

ou

Sub fusion2()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  Set f1 = Sheets("ca2014")
  a = f1.Range("A2:B" & f1.[a65000].End(xlUp).Row)
  Set f2 = Sheets("ca2015")
  b = f2.Range("A2:B" & f2.[a65000].End(xlUp).Row)
  For i = LBound(a) To UBound(a): d1(a(i, 1)) = "": d2(a(i, 1)) = "": Next i
  For i = LBound(b) To UBound(b): d1(b(i, 1)) = "": d2(b(i, 1)) = "": Next i
  For i = LBound(a) To UBound(a): d1(a(i, 1)) = a(i, 2): Next i
  For i = LBound(b) To UBound(b): d2(b(i, 1)) = b(i, 2): Next i
  Sheets("fusion").[A2].Resize(d1.Count) = Application.Transpose(d1.keys)
  Sheets("fusion").[b2].Resize(d1.Count) = Application.Transpose(d1.items)
  Sheets("fusion").[c2].Resize(d1.Count) = Application.Transpose(d2.items)
End Sub

Regroupement par lignes avec sous-totaux (tableau + indexation par dictionnaire)

On regroupe les lignes suivant la 1ere colonne en effectuant un sous total.
On suppose que le tableau Tbl() n'est pas trié.
La table TblRes() est indexée par le dictionnaire pour permettre un accès plus rapide à chaque ligne de la table.

Regroupe Sous Total

Sub RegroupeSousTotall()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set f1 = Sheets("données")
  Tbl = f1.Range("A2:D" & f1.[A65000].End(xlUp).Row).Value
  Ncol = 4
  Dim TblRes(): ReDim TblRes(1 To UBound(Tbl), 1 To Ncol)
  For ligne = 1 To UBound(Tbl)
  clé = Tbl(ligne, 1)
  If d1.exists(clé) Then
     lig = d1(clé)
  Else
    d1(clé) = d1.Count + 1: lig = d1.Count ' index
    For k = 1 To 3: TblRes(lig, k) = Tbl(ligne, k): Next k
  End If
  TblRes(lig, 4) = TblRes(lig, 4) + Tbl(ligne, 4)
Next ligne
Set f2 = Sheets("résultats")
f1.[a1].Resize(, Ncol).Copy f2.[a1]
f2.[a2].Resize(d1.Count, Ncol) = TblRes
End Sub

On regroupe les lignes suivant la 1ere colonne en effectuant un sous total et un cacul du nombre de villes distinctes.

Regroupe Sous Total Nb villes distinctes
Regroupe Sous Total Nb villes distinctes MAC
Regroupement 2 Critères Saut de ligne 3

Sub RegroupeSousTotallNbVillesDistinctes()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  Set f1 = Sheets("données")
  Tbl = f1.Range("A2:D" & f1.[A65000].End(xlUp).Row).Value
  Ncol = 5
  Dim TblRes(): ReDim TblRes(1 To UBound(Tbl), 1 To Ncol)
  For ligne = 1 To UBound(Tbl)
    clé = Tbl(ligne, 1)
    If d1.exists(clé) Then
      lig = d1(clé)
    Else
      d1(clé) = d1.Count + 1: lig = d1.Count ' index
      For k = 1 To 2: TblRes(lig, k) = Tbl(ligne, k): Next k
    End If
    TblRes(lig, 3) = TblRes(lig, 3) + Tbl(ligne, 4)
     clé2 = Tbl(ligne, 1) & "|" & Tbl(ligne, 3)
     If Not d2.exists(clé2) Then
       TblRes(lig, 4) = TblRes(lig, 4) + 1
       TblRes(lig, 5) = TblRes(lig, 5) & Tbl(ligne, 3) & " ": d2(clé2) = ""
     End If
  Next ligne
  Set f2 = Sheets("résultats")
   f2.[a2].Resize(d1.Count, Ncol) = TblRes
End Sub

Autres exemples

Regroupe Sous Total
Regroupe colonne avec saut de ligne
Regroupe colonne avec saut de ligne2
RegroupeSousTotal Plusieurs champs
RegroupeSousTotal Plusieurs champs clé 2 colonnes
RegroupeSousTotal Plusieurs champs 2
RegroupeSousTotal Plusieurs champs 3

Sub RegroupeLigneCumul()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set f1 = Sheets("données")
  Tbl = f1.[a1].CurrentRegion
  Ncol = UBound(Tbl, 2)
  Dim TblRes(): ReDim TblRes(1 To UBound(Tbl), 1 To Ncol)
  For ligne = 2 To UBound(Tbl)
     clé = Tbl(ligne, 1)
     If d1.exists(clé) Then lig = d1(clé) Else d1(clé) = d1.Count + 1: lig = d1.Count ' index
     TblRes(lig, 1) = clé
     For col = 2 To Ncol
       If Tbl(ligne, col) <> "" Then TblRes(lig, col) = TblRes(lig, col) + Val(Tbl(ligne, col))
     Next col
  Next ligne
  Set f2 = Sheets("résultats")
  f1.[a1].Resize(, Ncol).Copy f2.[a1]
  f2.[a2].Resize(d1.Count, Ncol) = TblRes
End Sub

Fusion de lignes doublons

On regroupe toutes les informations des doublons Nom+prénom dans une seule ligne.

Fusion lignes doublons
Fusion lignes doublons avec saut de ligne

Sub RegroupeLigneS()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set f1 = Sheets("BD")
  Set f2 = Sheets("résultats")
  ncol = f1.[a1].CurrentRegion.Columns.Count
  nlig = f1.[a1].CurrentRegion.Rows.Count
  d1.CompareMode = vbTextCompare
  For ligne = 1 To nlig
    crit = f1.Cells(ligne, 1) & f1.Cells(ligne, 2) ' nom+prenom
    d1(crit) = ""
    ligT = Application.Match(crit, d1.keys, 0)
    For col = 1 To ncol
      If f1.Cells(ligne, col) <> "" Then f2.Cells(ligT, col) = f1.Cells(ligne, col).Text
    Next col
    If f1.Cells(ligne, ncol) <> "" Then f1.Cells(ligne, ncol).Copy f2.Cells(ligT, ncol)
  Next ligne
End Sub

Sur cette version, nous fusionnons tous les numéros de tph de chaque personne dans une cellule.

Fusion lignes doublons2

Regroupement dans une cellule

Regroupe prénoms
Regroupe quartiers
Regroupement
Regroupement2
Regroupement avec saut de ligne
Regroupement 2 critères avec saut de ligne

Sub ListeSansDoublons()
   Set mondico = CreateObject("Scripting.Dictionary")
   For Each c In Range("a2", [a65000].End(xlUp))
     If Not mondico.exists(c.Value) Then
        mondico(c.Value) = c.Offset(, 1).Value
     Else
        mondico(c.Value) = mondico(c.Value) & "," & c.Offset(, 1).Value
     End If
  Next c
  [D2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
  [E2].Resize(mondico.Count, 1) = Application.Transpose(mondico.items)
End Sub

Regroupement avec présentation en colonnes

Sub PrésentationColonnes()
  [D2:K100].ClearContents
  Set d = CreateObject("Scripting.Dictionary")
  For Each c In Range("A2:A" & [A65000].End(xlUp).Row)
    d(c.Value) = d(c.Value) & c.Offset(0, 1) & "|"
  Next c
  If d.Count > 0 Then
    [d2].Resize(, d.Count) = d.keys
    i = 0
    For Each c In d.items
       a = Split(c, "|")
       d4].Offset(, i).Resize(UBound(a)) = Application.Transpose(a)
       i = i + 1
    Next c
  End If
End Sub

Statistiques 2 critères

Stat 2 Critères Totaux
Stat 2 Critères Totaux MAC
Stat 2 Critères Totaux Trié
Stat 2 Critères Maximum
Regroupement 2 Critères Saut de ligne
Regroupement 2 Critères Saut de ligne 2

Sub Stat2DTab()
  Set f = Sheets("BD")
  TblBD = f.Range("A2:C" & f.[A65000].End(xlUp).Row).Value ' Array pour rapidité
  colCrit1 = 1: colCrit2 = 2: colOper = 3
  Set Result = f.Range("f1")     ' Adresse résultat
  Set d1 = CreateObject("Scripting.Dictionary") ' Dictionnaire index pour rapidité
  Set d2 = CreateObject("Scripting.Dictionary")
  Dim TblTot(): ReDim TblTot(1 To UBound(TblBD), 1 To UBound(TblBD, 2))
  Dim TblTotLig(): ReDim TblTotLig(1 To UBound(TblBD))
  Dim TblTotCol(): ReDim TblTotCol(1 To UBound(TblBD, 2))
  For i = LBound(TblBD) To UBound(TblBD)
    clé1 = TblBD(i, colCrit1): If d1.exists(clé1) Then lig = d1(clé1) Else d1(clé1) = d1.Count + 1: lig = d1.Count
    clé2 = TblBD(i, colCrit2): If d2.exists(clé2) Then col = d2(clé2) Else d2(clé2) = d2.Count + 1: col = d2.Count
    TblTot(lig, col) = TblTot(lig, col) + TblBD(i, colOper)
    TblTotLig(lig) = TblTotLig(lig) + TblBD(i, colOper)
    TblTotCol(col) = TblTotCol(col) + TblBD(i, colOper)
  Next i
  Result.Offset(1).Resize(d1.Count, 1) = Application.Transpose(d1.keys) ' titre lignes
  Result.Offset(, 1).Resize(1, d2.Count) = d2.keys ' titres colonnes
  Result.Offset(1, 1).Resize(d1.Count, d2.Count) = TblTot ' stat 2D
  Result.Offset(d1.Count + 1, 1).Resize(, d2.Count) = TblTotCol ' totaux colonnes
  Result.Offset(1, d2.Count + 1).Resize(d1.Count) = Application.Transpose(TblTotLig) ' totaux lignes
End Sub

Avec des fonctions standards (cf programme)

Sub stats()
  tablo = [A2:C11]
  a = Stat2DSomme(tablo, 1, 2, 3)         ' Adapter avec les colonnes choisies
  [F2].Resize(UBound(a), UBound(a, 2)) = a
End Sub

Autre exemple

Rapport 2 Critères croisé
Simul TCD

Sub Stat2DTab()
  Set f = Sheets("BD")
  TblBD = f.Range("A2:C" & f.[A65000].End(xlUp).Row).Value ' Array pour rapidité
  colCrit1 = 1: colCrit2 = 3: colOper = 2
  Set AdrResult = f.Range("f1")                        ' Adresse résultat
  Set d1 = CreateObject("Scripting.Dictionary") ' Dictionnaire index pour rapidité
  Set d2 = CreateObject("Scripting.Dictionary")
  Dim TblRes(1 To 100, 1 To 100)
  For i = LBound(TblBD) To UBound(TblBD)
    clé1 = TblBD(i, colCrit1): If d1.exists(clé1) Then lig = d1(clé1) Else d1(clé1) = d1.Count + 1: lig = d1.Count
    clé2 = TblBD(i, colCrit2): If d2.exists(clé2) Then col = d2(clé2) Else d2(clé2) = d2.Count + 1: col = d2.Count
    TblRes(lig, col) = TblBD(i, colOper)
  Next i
  AdrResult.Offset(1).Resize(d1.Count, 1) = Application.Transpose(d1.keys) ' titre lignes
  AdrResult.Offset(, 1).Resize(1, d2.Count) = d2.keys             ' titres colonnes
  AdrResult.Offset(1, 1).Resize(d1.Count, d2.Count) = TblRes  ' résultat
End Sub

Autre exemple

-La BD est transférée dans une table Tbl(,) pour la rapidité d'accès.
-Les stats sont effectuées dans un tableau Tstat()
-Pour retrouver plus rapidement la ligne et la colonne du tableau Tstat(,) où effectuer le cumul, on indexe celui ci avec 2 dictionnaires d1 et d2.

Stat 2 CritèresTotaux 2

Sub Stat2D()
  Set f1 = Sheets("data")
  Set d1 = CreateObject("Scripting.Dictionary") ' lignes
  Set d2 = CreateObject("Scripting.Dictionary") ' colonnes
  Set d3 = CreateObject("Scripting.Dictionary")
  Tbl = f1.Range("a2:d" & f1.[A65000].End(xlUp).Row)
  Dim Tstat(): ReDim Tstat(1 To UBound(Tbl), 1 To UBound(Tbl, 2) + 1)
  Dim Tcol: ReDim Tcol(1 To UBound(Tbl, 2) + 1): Dim Tlig(): ReDim Tlig(1 To UBound(Tbl)) ' totaux lignes &     colonnes
  For i = 1 To UBound(Tbl)
     clé1 = Tbl(i, 1): clé2 = Tbl(i, 3)
     If d1.exists(clé1) Then lig = d1(clé1) Else d3(Tbl(i, 2)) = "": d1(clé1) = d1.Count + 1: lig = d1.Count
     If d2.exists(clé2) Then col = d2(clé2) Else d2(clé2) = d2.Count + 1: col = d2.Count
     Tstat(lig, col) = Tstat(lig, col) + Tbl(i, 4)
     Tlig(lig) = Tlig(lig) + Tbl(i, 4)
     Tcol(col) = Tcol(col) + Tbl(i, 4)
  Next i
  Set résult = Sheets("synthèse").Range("a1")
  résult.Offset(1).Resize(d1.Count, 1) = Application.Transpose(d1.keys) ' titres lignes
  résult.Offset(1, 1).Resize(d3.Count, 1) = Application.Transpose(d3.keys)
  résult.Offset(, 2).Resize(1, d2.Count) = d2.keys                                 ' titres colonnes
  résult.Offset(1, 2).Resize(d1.Count, d2.Count) = Tstat
  résult.Offset(d1.Count + 1, 2).Resize(, d2.Count) = Tcol                                 ' totaux colonnes
  résult.Offset(1, d2.Count + 2).Resize(d1.Count) = Application.Transpose(Tlig)  ' totaux lignes
  '-- tri lignes
  résult.Offset(1).Resize(d1.Count, d2.Count + 3).Sort key1:=[a2], Header:=xlNo
End Sub

Autre Exemple

On regroupe des icônes en conservant les polices et les couleurs.

Regroupe Icônes

Sub RegroupeIcônes()
  Set f = Sheets("BD")
  BD = f.Range("A2:E" & f.[A65000].End(xlUp).Row).Value    ' 2 colonnes de plus pour les attributs
  colCrit1 = 1
  colCrit2 = 2
  colOper = 3
  colAttribut = 4
  Set AdrRes = f.Range("G1")                ' Adresse résultat
  For i = LBound(BD) To UBound(BD)     ' Remplissage des Attributs
    BD(i, colAttribut) = f.Cells(i + 1, colOper).Font.ColorIndex
    BD(i, colAttribut + 1) = f.Cells(i + 1, colOper).Font.Name
  Next i
  AdrRes.CurrentRegion.ClearContents
  Set d1 = CreateObject("Scripting.Dictionary")   ' Dictionnaire index pour rapidité
  Set d2 = CreateObject("Scripting.Dictionary")
  For i = LBound(BD) To UBound(BD)
    tmp = BD(i, colCrit1): If d1.exists(tmp) Then lig = d1(tmp) Else d1(tmp) = d1.Count + 1: lig = d1.Count
    tmp = BD(i, colCrit2): If d2.exists(tmp) Then col = d2(tmp) Else d2(tmp) = d2.Count + 1: col = d2.Count
    x = Len(AdrRes.Offset(lig, col))
    AdrRes.Offset(lig, col).Characters(Start:=x + 1, Length:=1).Text = BD(i, colOper)
    AdrRes.Offset(lig, col).Characters(Start:=x + 1, Length:=1).Font.ColorIndex = BD(i, colAttribut)
    AdrRes.Offset(lig, col).Characters(Start:=x + 1, Length:=1).Font.Name = BD(i, colAttribut + 1)
  Next i
  AdrRes.Offset(1).Resize(d1.Count) = Application.Transpose(d1.keys)  ' titres lignes
  AdrRes.Offset(, 1).Resize(, d2.Count) = d2.keys                               ' titres colonnes
  '--- tri lignes & colonnes
  Set Rng = AdrRes.Resize(d1.Count + 1, d2.Count + 1)
  Rng.Offset(1).Resize(Rng.Rows.Count - 1, Rng.Columns.Count).Sort key1:=Rng.Cells(2, 1), _
    Order1:=xlAscending, Header:=xlNo, MatchCase:=False, Orientation:=xlSortColumns
  Rng.Offset(, 1).Resize(Rng.Rows.Count, Rng.Columns.Count - 1).Sort key1:=Rng.Cells(1, 2), _
    Order1:=xlAscending, Header:=xlNo, MatchCase:=False, Orientation:=xlSortRows
End Sub

Regroupe Couleurs

Fonction de consolidation Multi-Zones

Cette fonction permet de consolider plusieurs tableaux dans un autre.

FonctionMergeMZ

Function MergeMZ(champ)
  Application.Volatile
  Set d = CreateObject("Scripting.Dictionary")
  For i = 1 To champ.Areas.Count ' parcours des zones du champ multi-zones
    For j = 1 To champ.Areas(i).Rows.Count ' parcours des éléments d'une zone
       If champ.Areas(i).Cells(j, 1) <> "" Then
          temp = champ.Areas(i).Cells(j, 1)
         d.Item(temp) = d.Item(temp) + champ.Areas(i).Cells(j, 2)
       End If
    Next j
  Next i
  Dim b()
  ReDim b(1 To Application.Caller.Rows.Count, 1 To 2) ' table pour retour
  i = 0
  For Each c In d.keys
    i = i + 1
    b(i, 1) = c
    b(i, 2) = d(c)
  Next c
  MergeMZ = b
End Function

Transformation d'un tableau en BD

TransformationTableauBD

Sub transforme()
  Set d1 = CreateObject("scripting.dictionary")
  For Each c In Range("a2:a" & [a65000].End(xlUp).Row)
    a = Split(c.Offset(, 1).Value, ";")
    For Each m In a: d1(Trim(m)) = c: Next m
  Next c
  [E2].Resize(d1.Count) = Application.Transpose(d1.keys)
  [D2].Resize(d1.Count) = Application.Transpose(d1.items)
End Sub

Transformation d'une BD en Tableau

TransformeBDTableau

Sub Regroupe2()
  Set d = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    d(c.Value) = d(c.Value) & c.Offset(0, 1) & "|" & c.Offset(0, 2) & "|"
  Next c
  [F2].Resize(d.Count) = Application.Transpose(d.keys)
  [G2].Resize(d.Count) = Application.Transpose(d.items)
  Application.DisplayAlerts = False
  Range("G2").Resize(d.Count).TextToColumns Other:=1, OtherChar:="|"
  Cells.EntireRow.AutoFit
End Sub

Autre exemple

BD Tableau

Sub BDTableau()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  Dim Tbl(1 To 100, 1 To 100)
  For Each c In Range("a2:a" & [A65000].End(xlUp).Row)
    If d1.exists(c.Value) Then lig = d1(c.Value) Else d1(c.Value) = d1.Count + 1: lig = d1.Count
    tmp = c.Offset(, 1)
    If d2.exists(tmp) Then col = d2(tmp) Else d2(tmp) = d2.Count + 1: col = d2.Count
    Tbl(lig, col) = c.Offset(, 2)
  Next c
  [f2].Resize(d1.Count, 1) = Application.Transpose(d1.keys)
  [g1].Resize(1, d2.Count) = d2.keys
  [g2].Resize(d1.Count, d2.Count) = Tbl
End Sub

Performance

Pour tester la performance de l'objet Dictionary, nous créons une liste de nombres aléatoires sans doublons
Nous obtenons un temps de 0,04 seconde pour 10.000 items

Sub Dictionnaire()
  t = Timer
  Set mondico = CreateObject("Scripting.Dictionary")
  n = 10000
  For i = 1 To n
    aléa = Int(Rnd * 10000)
    mondico(aléa) = mondico(aléa) + 1
  Next i
  MsgBox Timer - t
  [A1].Resize(mondico.Count) = Application.Transpose(mondico.keys)
  [B1].Resize(mondico.Count) = Application.Transpose(mondico.items)
End Sub

Comparaison Dictionary/Collection

Sub DicoSansDoublons()
  t = Timer()
  Set mondico = CreateObject("Scripting.Dictionary")
  n = 40000
  For i = 1 To n
    temp = Int(Rnd * n)
    mondico(temp) = ""
  Next i
  [a2].Resize(mondico.Count) = Application.Transpose(mondico.keys)
  MsgBox Timer() - t ' 0,17 sec
End Sub

Sub CollectionSansDoublons()
   Dim t, i As Long, n As Long, temp As Long
   t = Timer()
   Dim Maliste As New Collection
   On Error Resume Next
   n = 40000
   For i = 1 To n
     temp = Int(Rnd * n)
     Maliste.Add Item:=temp, key:=CStr(temp)
   Next i
   On Error GoTo 0
   Dim a()
   ReDim a(1 To n)
   For i = 1 To Maliste.Count
     a(i) = Maliste(i)
   Next i
   [a2].Resize(Maliste.Count) = Application.Transpose(a)
   MsgBox Timer() - t ' 10 sec
End Sub

Ne surtout pas trier directement une collection (32 sec pour 1.200 éléments). Il faut passer par un tableau.

Comparaison Dictionary/Tableau/Find

L'accès à une clé d'un dictionnaire est 100 fois + rapide qu'une recherche séquentielle dans un tableau (l'accès aux clés d'un dictionnaire doit se faire par hash-code)

CompareTableauDictionary
Compare Dictionary Collection Recherche Dichotomique

Sub RechercheTableau()
  a = [A1:b20000]
  t = Timer()
  For j = 15000 To 16000 Step 2 ' 500 recherches= 4 sec
    x = "Nom" & Trim(Str(j))
    For i = 1 To 20000
      If a(i, 1) = x Then
        y = a(i, 2)
      End If
     Next i
   Next j
   MsgBox Timer() - t
End Sub

Sub RechercheDico()
  Set mondico = CreateObject("scripting.dictionary")
  a = [A1:b20000]
  For i = 1 To 20000
    mondico(A(i, 1)) = A(i, 2)
  Next i
  t = Timer()
  For j = 15000 To 16000 Step 2 ' 500 recherches= 0,015 sec
    x = "Nom" & Trim(Str(j))
    y = mondico(x)
  Next j
  MsgBox Timer() - t
End Sub

Sub RechercheFind()
  Set mondico = CreateObject("scripting.dictionary")
  t = Timer()
  For j = 15000 To 16000 Step 2 ' 500 recherches= 2,65 sec
    x = "Nom" & Trim(Str(j))
    Set result = [A1:A20000].Find(what:=x)
       y = result.Offset(, 1)
   Next j
  MsgBox Timer() - t
End Sub

Fonction perso RechvM() matricielle plus rapide que Recherchev() classique

RechvM
RechvMultCol

Si on modifie les 2.600 valeurs cherchées dans un tableau de 20.000 items,
le temps de recalcul est de 0,2 seconde (3,9 sec pour recherchev())

-Sélectionner G2:G2673
=RechvM(F2:F2673;matable;2)
-Valider avec maj+ctrl+entrée

Function RechvM(clé As Range, champ As Range, colResult)
  Application.Volatile
  Set d = CreateObject("Scripting.Dictionary")
  a = champ.Value
  b = clé.Value
  For i = LBound(a) To UBound(a)
    d(a(i, 1)) = a(i, colResult)
  Next i
  Dim temp()
  ReDim temp(LBound(b) To UBound(b))
  For i = LBound(b) To UBound(b)
    temp(i) = d(b(i, 1))
  Next i
  RechvM = Application.Transpose(temp)
End Function

Cette version renvoi un message d'erreur si la valeur cherchée n'existe pas.

Function RechvM(clé As Range, champ As Range, colResult, messageErreur)
  Application.Volatile
  Set d = CreateObject("Scripting.Dictionary")
  a = champ.Value
  b = clé.Value
  For i = LBound(a) To UBound(a)
    d(a(i, 1)) = a(i, colResult)
  Next i
  Dim temp()
  ReDim temp(LBound(b) To UBound(b))
  For i = LBound(b) To UBound(b)
    If d(b(i, 1)) <> "" Then temp(i) = d(b(i, 1)) Else temp(i) = messageErreur
  Next i
  RechvM = Application.Transpose(temp)
End Function

Recherchev avec 2 critères

Rechv 2 critères

Function Rechv2Crit(clé1 As Range, clé2 As Range, Clé1Tbl As Range, Clé2Tbl As Range, RésultTbl As Range, messageErreur)
  Application.Volatile
  Set d = CreateObject("Scripting.Dictionary")
  a = RésultTbl.Value
  b = Clé1Tbl.Value
  c = Clé2Tbl.Value
  TbClé1 = clé1.Value
  TbClé2 = clé2.Value
  For i = LBound(a) To UBound(a)
    d(b(i, 1) & "|" & c(i, 1)) = a(i, 1)
  Next i
  Dim temp(): ReDim temp(LBound(TbClé1) To UBound(TbClé1), 1 To 1)
  For i = LBound(TbClé1) To UBound(TbClé1)
    If d.exists(TbClé1(i, 1) & "|" & TbClé2(i, 1)) Then temp(i, 1) = d(TbClé1(i, 1) & "|" & TbClé2(i, 1)) Else temp(i, 1) =       messageErreur
  Next i
  Rechv2Crit = temp
End Function

Dictionary pour remplacer Sommeprod()

Comment améliorer Sommeprod() lorsque cette fonction

- travaille sur des champs de taille importante
- est recopiée x1000 fois

Sur l'exemple en PJ, avec une fonction perso matricielle, on passe d'un temps de recalcul de 3 sec à 0,05 sec pour 4.000 lignes

=SOMMEPROD((dates=A2)*(numero=B2)) ou =CombienFois(numero; dates)

CombienPerso
CombienSommeProd
MatricielPerso

Alimenter une liste déroulante triée sans les doublons

On alimente un combobox avec une liste triée sans doublons.

FormLSD

Option Compare Text
Dim f, dico
Private Sub UserForm_Initialize()
  Set f = Sheets("BD")
  Set dico = CreateObject("Scripting.Dictionary")
  For Each c In f.Range("B2:B" & f.[B65000].End(xlUp).Row)
     dico(c.Value) = ""
  Next c
  temp = dico.keys
  Call tri(temp, LBound(temp), UBound(temp))
  Me.ComboBox1.List = temp
End Sub

Private Sub ComboBox1_Change()
  Me.ListBox1.Clear
  For Each c In f.Range("B2:B" & f.[B65000].End(xlUp).Row)
     If c = Me.ComboBox1 Then Me.ListBox1.AddItem c.Offset(, -1)
  Next c
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
       temp = a(g): a(g) = a(d): a(d) = temp
       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 lecture des données du tableur peut être accélérée en utilisant un tableau a()

Private Sub UserForm_Initialize()
  Set f = Sheets("BD")
  Set MonDico = CreateObject("Scripting.Dictionary")
  a = f.Range("B2:B" & f.[B65000].End(xlUp).Row)     ' tableau (n,1)
  For i = LBound(a) To UBound(a)
    MonDico(a(i, 1)) = ""
  Next i
  temp = MonDico.keys
  Call tri(temp, LBound(temp), UBound(temp))
  Me.ComboBox1.List = temp
End Sub

S'il y a des Majuscules/Minuscules

MonDico(Ucase(c.Value) )= ""

ou

MonDico(Application.Proper(c.Value) )= ""

Simulation de l'objet Dictionary pour Excel Mac

Principe de la simulation de Dictionary avec 2 collections

Simulation dico avec collection

Sub SimulDictionnareCollection()
  Dim CollecItem As New Collection, CollecCle As New Collection
  nom = "Dupond": age = 30
  CollecCle.Add Item:=nom, Key:=nom
  CollecItem.Add Item:=age, Key:=nom
  nom = "Martin": age = 35
  CollecCle.Add Item:=nom, Key:=nom
  CollecItem.Add Item:=age, Key:=nom
  For i = 1 To CollecCle.Count       ' Toutes les clés et items
    Cells(i + 1, 1) = CollecCle(i)
    Cells(i + 1, 2) = CollecItem(CollecCle(i))
  Next i
End Sub

Pour simuler l'objet Dictionary sur Excel Mac, on crée un module de classe DictionnaireMac.

ClasseDictionnaireMacCollection
ClasseDictionnaireMacTableau

Méthodes et propriétés

Dico.Ajout clé,item

Ajoute une clé et la valeur associée

Dico.Existe(clé)

Teste l'existence d'une clé

Dico.Item(clé)

Donne l'item de la clé

Dico.Count(clé)

Donne le nombre de clés

Dico.Sup clé

Suprime la clé

Dico.ListeCles

Renvoie un tableau vertical des clés

Dico.ListeItems

Renvoie un tableau vertical des items

Dico.Cle(indice)

Donne la clé pour un indice (1,2,3,...)

Dico.Tri

Tri les clés

Exemple 1: Sur cet exemple, nous obtenons une liste sans doublons.

Sur l'exemple, nous créons un dictionnaire avec les noms comme clés et les villes comme items.
Les doublons de Nom1 seront éliminés.

Noms Villes
Nom1 Ville1
Nom2 Ville2
Nom3 Ville3
Nom4 Ville4
Nom1 Ville1
Nom1 Ville1
Nom7 Ville7

Code

Sub ListeSansDoublonsCollection()
  Set d1 = New DictionnaireMac
  Set plage1 = Range("A2", [a65000].End(xlUp))
  For Each c In plage1
      If c <> "" Then d1.ajout c.Value, c.Offset(, 1).Value
  Next c
  '---- transfert dans le tableur
  Range("d2").Resize(d1.count) = d1.listeCles
  Range("e2").Resize(d1.count) = d1.listeItems
  [P2].Resize(d1.count, 2) = d1.Tri
End Sub

Module de classe DictionnaireMac

L'option Key de la classe Collection permet d'interdire les doublons à la création et d'accéder à un item par une clé mais elle ne permet pas d'accéder à l'ensemble des clés. Pour accéder à l'ensemble des clés, nous utilisons une deuxième collection (CollecCle).
L'objet Collec stocke les items et l'objet CollecCle les clés

Private xn
Private Collec As New Collection
Private CollecCle As New Collection

Sub ajout(cle, item)
  On Error Resume Next
  Collec.Add item:=item, Key:=cle
  CollecCle.Add item:=cle, Key:=cle
  If Err = 0 Then xn = xn + 1
End Sub

Public Property Get count()
  count = xn
End Property

Public Property Get listeItems()
  Dim temp()
  ReDim temp(1 To xn)
  For i = 1 To xn
    temp(i) = Collec(i)
  Next i
  listeItems = Application.Transpose(temp)
End Property

Public Property Get listeCles()
  Dim temp()
  ReDim temp(1 To xn)
  For i = 1 To xn
    temp(i) = CollecCle(i)
  Next i
  listeCles = Application.Transpose(temp)
End Property

Public Property Get item(cle)
  item = Collec(cle)
End Property

Public Property Get Existe(cle)
  On Error Resume Next
  retour = Collec(cle)
  Existe = (Err = 0)
End Property

Public Property Get cle(indice)
   If indice <= xn Then cle = CollecCle(indice) Else cle = ""
End Property

Public Property Get ItemInd(indice)
   If indice <= xn Then ItemInd = Collec(indice) Else ItemInd = ""
End Property

Sub Sup(cle)
  p = 0
  For i = 1 To xn
    If CollecCle(i) = cle Then p = i
  Next i
  If p > 0 Then
    CollecCle.Remove p
    Collec.Remove p
    xn = xn - 1
  End If
End Sub

Function Tri()
  Dim temp()
  ReDim temp(1 To xn, 1 To 2)
  For i = 1 To xn
    temp(i, 1) = CollecCle(i)
    temp(i, 2) = Collec(i)
  Next i
  Call Quick(temp, LBound(temp), UBound(temp))
  Tri = temp
End Function

Sub Quick(a, gauc, droi) ' Quick sort
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
temp = a(g, 1): a(g, 1) = a(d, 1): a(d, 1) = temp
temp = a(g, 2): a(g, 2) = a(d, 2): a(d, 2) = temp
g = g + 1: d = d - 1
End If
Loop While g <= d
If g < droi Then Call Quick(a, g, droi)
If gauc < d Then Call Quick(a, gauc, d)
End Sub

Classe Dictionnaire

Cette classe encapsule la classe Dictionary en lui ajoutant un Tri.
Elle peut remplacer SortedList. Elle permet par exemple:
- d'obtenir des listes triées sans doublon (0,36s pour 10.000 items),
- d'effectuer des tris multi-critères de tableaux à 2 dimensions,...

Classe Dictionnaire Tri
Classe DictionarySorted David84

Classe Base de données

La classe BD gère des enregistrements.
Elle permet d'ajouter, supprimer et trier des enregistrements.
Construite avec l'objet Dictionary, elle permet d'accéder directement (rapidement) à un engistrement par une clé. Elle peut remplacer avantageusement des tableaux de taille importante.

ClasseBD Dictionary
ClasseBD Collection

Repérage de doublons

On veut colorier les doublons.

Sub ColoriageDoublons()
  [A:A].Interior.ColorIndex = xlNone
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    If c<>"" then  mondico.Item(c.Value) = mondico.Item(c.Value) + 1
  Next c
  For Each c In Range("a2", [a65000].End(xlUp))
    If mondico.Item(c.Value) > 1 Then c.Interior.ColorIndex = 4
  Next c
End Sub

Pour 12.000 lignes, avec la fonction Nb.Si(), on obtient un temps de 15 secondes au lieu de 0,5 seconde avec l'objet Dictionary.

Sub ColoriageDoublonsNbSi
  Application.ScreenUpdating = False
  [a:a].Interior.ColorIndex = xlNone
  For Each c In Range("a2", [a65000].End(xlUp))
     If Application.CountIf([a2:a12000], c) > 1 Then c.Interior.ColorIndex = 4
  Next c
End Sub

Ci dessous, chaque groupe a une couleur différente

ColorGroup
ColorGroup2
ColorGroupComment
ColoriageDoublonsCommentMultiFeuilles

Sub GroupColor()
  couleurs = Array(1, 3, 4, 6, 7, 8, 14, 15, 17, 20, 22, 24, 26, 27, 28, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 50, 53)
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    If c <> "" Then mondico.Item(c.Value) = mondico.Item(c.Value) + 1
  Next c
  For Each c In Range("a2", [a65000].End(xlUp))
   If c <> "" Then
     nocoul = (Application.Match(c.Value, mondico.keys, 0)) Mod UBound(couleurs)
     If mondico.Item(c.Value) > 1 Then c.Interior.ColorIndex = couleurs(nocoul)
   End If
  Next c
End Sub

Doublons 2 critères

Le test de doublon se fait sur les colonnes A et C.

Doublons 2 Critères

Sub GroupColor2CritèresColAColC()
  couleurs = Array(1, 3, 4, 6, 7, 8, 14, 15, 17, 20, 22, 24, 26, 27, 28, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44,    45, 46, 50, 53)
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    clé = c.Value & c.Offset(, 2)
    mondico.Item(clé) = mondico.Item(clé) + 1
  Next c
  For Each c In Range("a2", [a65000].End(xlUp))
    clé = c.Value & c.Offset(, 2)
    nocoul = (Application.Match(clé, mondico.keys, 0)) Mod UBound(couleurs)
    If mondico.Item(clé) > 1 Then c.Resize(, 4).Interior.ColorIndex = couleurs(nocoul)
  Next c
End Sub

Doublons 2 critères avec indication des nos de lignes

Color Group 1 critère commentaire
Color Group 1 critère HyperLien
Color Group 2 critères commentaire
Color Group 2 critères2

     

Sub GroupHyperLien()
  Application.ScreenUpdating = False
  Set Rng = Range("A2", [A65000].End(xlUp))
  Rng.Offset(, 1).Resize(, 5).Clear
  Rng.Interior.ColorIndex = xlNone
  couleurs = Array(3, 4, 6, 7, 8, 14, 15, 17, 20, 22, 24, 26, 27, 28, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46,    50, 53)
  Set d = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  For Each c In Rng
    If c <> "" Then
      d.Item(c.Value) = d.Item(c.Value) + 1
      d2.Item(c.Value) = d2.Item(c.Value) & CStr(c.Row) & "|"
    End If
  Next c
  nf = ActiveSheet.Name
  For Each c In Rng
   If c.Value <> "" Then
     If d.Item(c.Value) > 1 Then
        nocoul = (Application.Match(c.Value, d.keys, 0)) Mod UBound(couleurs)
        c.Interior.ColorIndex = couleurs(nocoul)
        temp = c.Value
        b = Split(d2(temp), "|")
        For k = 0 To UBound(b) - 1
          ActiveSheet.Hyperlinks.Add Anchor:=Cells(c.Row, k + 2), Address:="", SubAddress:="'" & nf & "'" & "!A" &            b(k), TextToDisplay:=b(k)
        Next k
      End If
    End If
  Next c
End Sub

Fonction liste sans doublons triée

Cette fonction personalisée matricielle donne une liste triée sans doublons (x100 fois +rapide qu'un tri matriciel)

Dans le tableur

-Sélectionner D2:D14
=sansdoublonstrié(A2:B11)
-valider avec maj+Ctrl+Entrée

Fonction Sans Doublons Triée
FonctionTri
FonctionSansDoublonsTriéeMultiZones
FonctionSansDoublonsTriéeMultiFeuilles

Option Compare Text
Function SansDoublonsTrié(champ As Range)
  Set mondico = CreateObject("Scripting.Dictionary")
  temp = champ
  For Each c In temp
    If c <> "" Then mondico(c) = ""
  Next c
  Dim b()
  ReDim b(1 To Application.Caller.Rows.Count)
  i = 1
  For Each c In mondico.keys
     b(i) = c
     i = i + 1
  Next
  Call tri(b, 1, mondico.Count)
  SansDoublonsTrié = Application.Transpose(b)
End Function

Repérer les doublons entre 2 colonnes

Repérage des doublons entre 2 colonnes

Doublons 2 colonnesSimple

Sub DoublonsRapide2col()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  Set plage1 = Range("A1", [a65000].End(xlUp))
  Set plage2 = Range("B1", [B65000].End(xlUp))
  [A:B].Interior.ColorIndex = xlNone
  For Each c In plage1
    If c <> "" Then d1(c.Value) = ""
  Next c
  For Each c In plage2
    If d1.exists(c.Value) Then c.Interior.ColorIndex = 3
    If c <> "" Then d2(c.Value) = ""
  Next c
  For Each c In plage1
    If d2.exists(c.Value) Then c.Interior.ColorIndex = 4
  Next c
End Sub

Repérage de tous les doublons (entre 2 colonnes + doublons dans chaque colonne)

Sub DoublonsRapideTous()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  Set plage1 = Range("A1", [a65000].End(xlUp))
  Set plage2 = Range("B1", [B65000].End(xlUp))
  [A:B].Interior.ColorIndex = xlNone
  For Each c In plage1
     If c <> "" Then d1(c.Value) = d1(c.Value) + 1
  Next c
  For Each c In plage2
  If c <> "" Then d2(c.Value) = d2(c.Value) + 1
    If d1.exists(c.Value) Then c.Interior.ColorIndex = 3
  Next c
  For Each c In plage1
    If d2.exists(c.Value) Then c.Interior.ColorIndex = 4
    If d1(c.Value) > 1 Then c.Interior.ColorIndex = 4
  Next c
  For Each c In plage2
    If d2(c.Value) > 1 Then c.Interior.ColorIndex = 3
  Next c
End Sub

Chaque groupe de doublons a une couleur différente

Doublons 2 colonnesCoulDiff

Sub DoublonsEntre2ColonnesCoulDiff()
  Set d = CreateObject("Scripting.Dictionary")
  couleurs = Array(3, 4, 6, 7, 8, 15, 17, 20, 22, 24, 26, 27, 28, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 50, 53)
  Set plage1 = Range("A2:A" & [a65000].End(xlUp).Row)
  Set plage2 = Range("B2:B" & [b65000].End(xlUp).Row)
  Union(plage1, plage2).Interior.ColorIndex = xlNone
  For Each C In plage1
     d.Item(C.Value) = d.Item(C.Value) & C.Row & "-"
  Next C
  For Each C In plage2
     If d.exists(C.Value) Then
       nocoul = (Application.Match(C.Value, d.keys, 0)) Mod UBound(couleurs)
       C.Interior.ColorIndex = couleurs(nocoul)
       a = Split(d.Item(C.Value), "-")
       For k = LBound(a) To UBound(a) - 1
         tmp = a(k) - plage1.Row + 1
         plage1(tmp).Interior.ColorIndex = couleurs(nocoul)
       Next k
     End If
   Next C
End Sub

Doublons 2 colonneCommentaire

Indique les no des lignes qui contiennent des doublons.

Liste des doublons en colonne A et liste des doublons en colonne B

Sub ListeDoublonsColA()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  For Each c In Range([a2], [a65000].End(xlUp))
    If c <> "" And d1.exists(c.Value) Then d2(c.Value) = d1(c.Value) & c.Address & "-"
    d1(c.Value) = d1(c.Value) & c.Address & "-"
  Next c
  If d2.Count > 0 Then
    [J2].Resize(d2.Count, 1) = Application.Transpose(d2.keys)
    [K2].Resize(d2.Count, 1) = Application.Transpose(d2.Items)
  End If
End Sub

Liste des doublons entre 2 colonnes

Sub DoublonsEntre2ColonnesRapport2()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set plage1 = Range("a2:a" & [a65000].End(xlUp).Row)
  Set plage2 = Range("b2:b" & [b65000].End(xlUp).Row)
  For Each c In plage2
     d1.Item(c.Value) = d1.Item(c.Value) & c.Address & "-"
  Next c
  I = 2
  For Each c In plage1
    If d1.exists(c.Value) Then
       Cells(I, "P") = c
       Cells(I, "Q") = c.Address
       Cells(I, "R") = d1.Item(c.Value)
       I = I + 1
    End If
  Next c
End Sub

Fonction matricielle ExtraitCoul

Cette fonction personalisée matricielle extrait une liste des cellules de la couleur choisie.

Dans le tableur

-Sélectionner C2:C11
=ExtraitCoul(A2:A11)
-valider avec maj+Ctrl+Entrée

FonctionExtraitCoul
FonctionExtraitGras

Choix successifs (listes différence)

On ne peut pas choisir plusieurs fois la même option.

ChoixSuccessifs



Dim liste, n
Private Sub UserForm_Initialize()
  n = 4
  creelistedispo
End Sub

Sub creelistedispo()
  Set f = Sheets("BD")
  Set liste = CreateObject("Scripting.Dictionary")
  For Each c In f.Range("a2:a" & f.[a65000].End(xlUp).Row).Value
    liste(c) = ""
  Next
  For i = 1 To n
    If Me("combobox" & i).Value <> "" Then liste.Remove (Me("combobox" & i).Value)
  Next i
  For i = 1 To n: Me("ComboBox" & i).List = liste.keys: Next
End Sub

Private Sub ComboBox1_Click()
  creelistedispo
End Sub

Private Sub ComboBox2_Click()
  creelistedispo
End Sub

Private Sub ComboBox3_Click()
  creelistedispo
End Sub

Private Sub ComboBox4_Click()
  creelistedispo
End Sub

Suppression de doublons dans une BD

0,23 sec pour 10.000 éléments

SupDoublonsDictionary

Sub SupDoublonsColA()
  Application.ScreenUpdating = False
  Set f1 = Sheets("BD")
  a = f1.Range("A1").CurrentRegion.Value
  Dim c()
  ReDim c(1 To UBound(a, 1), 1 To UBound(a, 2))
  ligne = 1
  Set mondico = CreateObject("Scripting.Dictionary")
  For i = 1 To UBound(a)
    If Not mondico.exists(a(i, 1)) Then
      mondico.Add a(i, 1), 1
      For k = 1 To UBound(a, 2): c(ligne, k) = a(i, k): Next k
      ligne = ligne + 1
    End If
  Next
  Sheets("resultat").[A1].Resize(mondico.Count, UBound(a, 2)) = c
End Sub

Suppression de lignes

On veut supprimer les lignes dont les 5 premiers caractères appartiennent à l'ensemble 01517,01521,...

SupLignes

Sub supLignes()
  Application.ScreenUpdating = False
  Set Dico = CreateObject("Scripting.Dictionary")
  For Each c In Sheets("trie").[A1].CurrentRegion: Dico(c.Text) = "": Next c
  i = 1
  Set f = Sheets("BD")
    Do While f.Cells(i, 1) <> ""
       If Dico.Exists(Left(f.Cells(i, 1), 5)) Then f.Rows(i).Delete Else i = i + 1
    Loop
End Sub

Liste des doublons

On veut la liste des éléments de la colonne A qui sont en double.

Set MonDico = CreateObject("Scripting.Dictionary")
Set MonDico2 = CreateObject("Scripting.Dictionary")
For Each c In Range([a2], [a65000].End(xlUp))
  If MonDico.exists(c.Value) Then MonDico2.Item(c.Value) = ""
  MonDico.Item(c.Value) = ""
Next c
If MonDico2.Count > 0 Then [E2].Resize(MonDico2.Count, 1) = Application.Transpose(MonDico2.keys)

Liste des doublons avec nos de lignes

Liste Doublons nos ligne

Sub ListeDoublons()
  Set d1 = CreateObject("Scripting.Dictionary")
  Set d2 = CreateObject("Scripting.Dictionary")
  For Each c In Range([a2], [a65000].End(xlUp))
     temp = Application.Trim(c.Value)
     If d1.exists(temp) Then
        If InStr(d2(temp), "|") = 0 Then
           d2.Item(temp) = d1(temp) & "|" & c.Row
         Else
           d2.Item(temp) = d2(temp) & "|" & c.Row
         End If
     Else
         d1.Item(temp) = c.Row
      End If
   Next c
   If d2.Count > 0 Then
      [E2].Resize(d2.Count, 1) = Application.Transpose(d2.keys)
      [F2].Resize(d2.Count, 1) = Application.Transpose(d2.items)
   End If
End Sub

liste des doublons sur 2 critères

Liste Doublons 2 critères

Sub ListeDoublons()
  Set MonDico = CreateObject("Scripting.Dictionary")
  Set mondico2 = CreateObject("Scripting.Dictionary")
  For Each c In Range([a2], [a65000].End(xlUp))
    tmp = c & " " & c.Offset(, 1)
    If MonDico.exists(tmp) Then mondico2.Item(tmp) = c.Row
   MonDico.Item(tmp) = ""
 Next c
 If mondico2.Count > 0 Then [E2].Resize(mondico2.Count, 1) = Application.Transpose(mondico2.keys)
 i = 2
 For Each c In mondico2.keys
   Cells(i, "g") = Cells(mondico2(c), 1)
   Cells(i, "h") = Cells(mondico2(c), 2)
    i = i + 1
 Next c
End Sub

Liste des non doublons (valeurs uniques)

Liste des non doublons (0,4sec pour 10.000 éléments)

Set mondico = CreateObject("Scripting.Dictionary")
For Each c In Range("a2", [a65000].End(xlUp))
  mondico.Item(c.Value) = mondico.Item(c.Value) + 1
Next c
Set mondico2 = CreateObject("Scripting.Dictionary")
For Each c In mondico.keys
   If mondico(c) = 1 Then mondico2(c) = ""
Next c
If mondico2.Count > 0 Then [c2].Resize(mondico2.Count, 1) = Application.Transpose(mondico2.keys)

Liste Non Doublons 3 critères

Sub NonDoublons()
  Set d = CreateObject("Scripting.Dictionary")
  Tbl = Range("a2:c" & [a65000].End(xlUp).Row)
  For i = LBound(Tbl) To UBound(Tbl)
     clé = Tbl(i, 1) & "|" & Tbl(i, 2) & "|" & Tbl(i, 3)
     d(clé) = d(clé) + 1
   Next i
   j = 0
   Dim b(): ReDim b(1 To d.Count, 1 To 3)
   For Each c In d.keys
      If d(c) = 1 Then
         j = j + 1
         a = Split(c, "|")
         b(j, 1) = a(0): b(j, 2) = a(1): b(j, 3) = a(2)
      End If
   Next c
   [e2].Resize(j, 3) = b
End Sub

Ou Exclusif entre 2 listes

On veut la liste de ceux qui n'existent pas dans les 2 listes

Ou Exclusif 2 listes

Compter le nombre de nos de conteneurs différents pour un service

FonctionNbSansDoublonsCritere

Dans le tableur, nous utilisons la fonction peronnalisée:

=NbSansdoublonsCritere($A$2:$A$22;$B$2:$B$22;D2)

Function NBSansDoublonsCritere(champ, champcritere, critere)
  Set MonDico = CreateObject("Scripting.Dictionary")
  For i = 1 To champ.Count
    If UCase(champcritere(i).Value) = UCase(critere) Then MonDico(champ(i).Value) = ""
  Next i
  NBSansDoublonsCritere = MonDico.Count
End Function

Eléments communs à 2 listes

0,5 seconde pour 2 listes de 10.000 éléments.

Eléments Communs
Eléments Communs BD
Eléments Communs2
Compare 2 Champs
Donnees Manquantes Liste

Sub Communs()
  a = Range("A2:A" & [A65000].End(xlUp).Row)
  Set MonDico1 = CreateObject("Scripting.Dictionary")
  For Each c In a
    MonDico1(c) = ""
  Next c
  b = Range("C2:C" & [C65000].End(xlUp).Row)
  Set MonDico2 = CreateObject("Scripting.Dictionary")
  For Each c In b
    If MonDico1.exists(c) Then If Not MonDico2.exists(c) Then MonDico2(c) = ""
  Next c
  [G2].Resize(MonDico2.Count, 1) = Application.Transpose(MonDico2.keys)
End Sub


Sub Fusion()
  Set MonDico = CreateObject("Scripting.Dictionary")
  a = Range("A2:A" & [A65000].End(xlUp).Row)
  b = Range("C2:C" & [C65000].End(xlUp).Row)
  For Each c In a
    MonDico(c) = ""
  Next c
  For Each c In b
    MonDico(c) = ""
  Next c
  [E2].Resize(MonDico.Count, 1) = Application.Transpose(MonDico.keys)
End Sub

Sub Liste2_Liste1()
  a = Range("A2:A" & [A65000].End(xlUp).Row)
  Set MonDico1 = CreateObject("Scripting.Dictionary")
  For Each c In a
    MonDico1(c) = ""
  Next c
  b = Range("C2:C" & [C65000].End(xlUp).Row)
  Set MonDico2 = CreateObject("Scripting.Dictionary")
  For Each c In b
    If Not MonDico1.exists(c) Then MonDico2(c) = ""
  Next c
  [I2].Resize(MonDico2.Count, 1) = Application.Transpose(MonDico2.keys)
End Sub

Sub Liste1_Liste2()
  a = Range("C2:C" & [C65000].End(xlUp).Row)
  Set MonDico1 = CreateObject("Scripting.Dictionary")
  For Each c In a
    MonDico1(c) = ""
  Next c
  b = Range("A2:A" & [A65000].End(xlUp).Row)
  Set MonDico2 = CreateObject("Scripting.Dictionary")
  For Each c In b
    If Not MonDico1.exists(c) Then MonDico2(c) = ""
  Next c
  [K2].Resize(MonDico2.Count, 1) = Application.Transpose(MonDico2.keys)
End Sub

Communs à 2 listes avec accents

Les mêmes noms sont orthographiés avec ou sans accent.

ComunsAccents

Sub Communs2Listes()
   Set f = Sheets("BD")
   Set d1 = CreateObject("Scripting.Dictionary")
   Set d2 = CreateObject("Scripting.Dictionary")
   For Each c In f.Range("A2:A" & [A65000].End(xlUp).Row): d1(sansAccent(c.Value)) = "": Next c
   For Each c In f.Range("c2:c" & [c65000].End(xlUp).Row)
     If d1.Exists(sansAccent(c.Value)) Then d2(sansAccent(c.Value)) = ""
   Next c
   f.[E2:E1000].ClearContents
   f.[E2].Resize(d2.Count) = Application.Transpose(d2.keys)
   f.[E2].Sort Key1:=f.[E2], Order1:=xlAscending, Header:=xlYes
End Sub

Function sansAccent(chaine)
  codeA = "ÉÈÊËÔéèêëàçùôûïî"
  codeB = "EEEEOeeeeacuouii"
  temp = chaine
  For i = 1 To Len(temp)
    p = InStr(codeA, Mid(temp, i, 1))
    If p > 0 Then Mid(temp, i, 1) = Mid(codeB, p, 1)
  Next
sansAccent = temp
End Function

Eléments communs à 2 listes avec 2 colonnes

Communs 2 listes
Communs 2 listes 2

Sub Communs()
  Set f1 = Sheets("feuil1")
  Set f2 = Sheets("feuil2")
  Set MonDico1 = CreateObject("Scripting.Dictionary")
  For Each c In f1.Range("A2:A" & f1.[A65000].End(xlUp).Row)
    MonDico1(c & " " & c.Offset(, 1)) = ""
  Next c
  Set MonDico2 = CreateObject("Scripting.Dictionary")
  For Each c In f2.Range("A2:A" & f2.[A65000].End(xlUp).Row)
    tmp = c & " " & c.Offset(, 1)
    If MonDico1.exists(tmp) Then If Not MonDico2.exists(tmp) Then MonDico2(tmp) = ""
  Next c
  f2.[E2].Resize(MonDico2.Count, 1) = Application.Transpose(MonDico2.keys)
End Sub

Eléments communs à plusieurs colonnes

On veut la liste des éléments communs aux colonnes d'un champ.

-Sélectionner G2:G8
=communs(A2:E12)
-Valider avec Maj+ctrl+entrée

Communs

Function communs(champ)
  Set mondico1 = CreateObject("Scripting.Dictionary")
  For Each c In Application.Index(champ, , 1)
    mondico1.Item(c.Value) = c.Offset(, 1).Value
  Next
  For col = 2 To champ.Columns.Count
    Set mondico2 = CreateObject("Scripting.Dictionary")
    For Each c In Application.Index(champ, , col)
      If mondico1.Exists(c.Value) Then mondico2(c.Value) = 1
    Next c
    Set mondico1 = mondico2
  Next col
  i = 1
  ReDim temp(1 To champ.Rows.Count)
  i = 1
  For Each c In mondico2.keys
    temp(i) = c
  i = i + 1
  Next
  communs = Application.Transpose(temp)
End Function

Liste des abréviations sans doublons

Sub Essai()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("B2", [B65000].End(xlUp))
    If Not mondico.Exists(c.Value) Then mondico.Add c.Value, Left(c.Offset(0, -1), InStr(c.Offset(0, -1), "-") - 1)
  Next c
  [E2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
  [F2].Resize(mondico.Count, 1) = Application.Transpose(mondico.items)
End Sub

Liste des d'items sans doublons et nombre d'items

Occurences

Sub Essai()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    mondico.Item(c.Value) = mondico.Item(c.Value) + 1
  Next c
  [c2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
  [d2].Resize(mondico.Count, 1) = Application.Transpose(mondico.items)
End Sub

Sub Essai2()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    mondico.Item(c.Value) = mondico.Item(c.Value) + 1
  Next c
  a = mondico.keys
  b = mondico.items
  For i = LBound(a) To UBound(a)
    Cells(i + 2, 6) = a(i) & "*" & b(i)
  Next i
End Sub

Sub Essai3()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    mondico.Item(c.Value) = mondico.Item(c.Value) + 1
  Next c
  i = 2
  For Each c In mondico.keys
    Cells(i, 8) = c & "*" & mondico.Item(c)
    i = i + 1
  Next c
End Sub

Sub Essai4()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("a2", [a65000].End(xlUp))
    mondico.Item(c.Value) = c.Value & " * " & Val(Right(mondico(c.Value), 3)) + 1
  Next c
  [j2].Resize(mondico.Count, 1) = Application.Transpose(mondico.items)
End Sub

Avec 2 critères

Sub compteOccurences2critères()
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In Range("A2", [A65000].End(xlUp))
    temp = c & "-" & c.Offset(, 1)
    mondico(temp) = IIf(mondico.exists(temp), mondico(temp) + 1, 1)
  Next c
  [e2].Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
  [f2].Resize(mondico.Count, 1) = Application.Transpose(mondico.items)
End Sub

Regroupement sur plusieurs colonnes & totalisation sur une colonne

Regroupe 4 colonnes & totalisation

Sub Regroupe()
  Set d = CreateObject("Scripting.Dictionary")
  Tbl = Range("A2:E" & [A65000].End(xlUp).Row).Value
  For i = LBound(Tbl) To UBound(Tbl)
    clé = Tbl(i, 1) & "-" & Tbl(i, 2) & "-" & Tbl(i, 3) & "-" & Tbl(i, 4)
    d(clé) = d(clé) + Tbl(i, 5)
  Next i
  [h2].Resize(d.Count) = Application.Transpose(d.keys)
  [i2].Resize(d.Count) = Application.Transpose(d.items)
End Sub

Regroupement des items de chaque code

Pour chaque code (en colonne A), on regroupe sur la même ligne tous les items(colonne B) associés au code,y compris les doublons. Une version MAC (sans Dictionary) est disponible dans Regroupe MAC.

Regroupe
Regroupe MAC
Regroupe sans doublons sur les items
Regroupe Transpose

Sub Regroupe()
  Set d = CreateObject("Scripting.Dictionary")
  Tbl = Range("A2:B" & [a65000].End(xlUp).Row).Value
  For i = LBound(Tbl) To UBound(Tbl)
   d(Tbl(i, 1)) = d(Tbl(i, 1)) & Tbl(i, 2) & "|"
  Next i
  [D2].Resize(d.Count) = Application.Transpose(d.keys)
  [E2].Resize(d.Count) = Application.Transpose(d.items)
  Application.DisplayAlerts = False
  Range("E2").Resize(d.Count).TextToColumns Other:=True, OtherChar:="|"
  Cells.EntireRow.AutoFit
End Sub

ou

Sub Regroupe2()
  Set d = CreateObject("Scripting.Dictionary")
  Tbl = Range("A2:B" & [a65000].End(xlUp).Row).Value
  For i = LBound(Tbl) To UBound(Tbl)
     d(Tbl(i, 1)) = d(Tbl(i, 1)) & Tbl(i, 2) & "|"
  Next i
  lig = 2
  For Each c In d.keys
    Cells(lig, "D") = c
    Tbl2 = Split(d.Item(c), "|")
    Cells(lig, "D").Offset(, 1).Resize(, UBound(Tbl2) + 1) = Application.Transpose(Application.Transpose(Tbl2))
    lig = lig + 1
  Next c
End Sub

ou

Sub regroupe3()
  Set d = CreateObject("Scripting.Dictionary")     ' index position de la clé dans TblD(): 1,2,3,..
  Tbl = Range("A2:B" & [A65000].End(xlUp).Row).Value
  Dim TblD(): ReDim TblD(1 To UBound(Tbl), 1 To 2)
  For i = 1 To UBound(Tbl)
     If Not d.exists(Tbl(i, 1)) Then d(Tbl(i, 1)) = d.Count + 1: TblD(d.Count, 1) = Tbl(i, 1)
     TblD(d(Tbl(i, 1)), 2) = TblD(d(Tbl(i, 1)), 2) & Tbl(i, 2) & "|"
  Next i
  [d2].Resize(d.Count, 2) = TblD
  Application.DisplayAlerts = False
  Range("E2").Resize(d.Count).TextToColumns Other:=True, OtherChar:="|"
End Sub

ou

Sub regroupe4()
  Set d1 = CreateObject("Scripting.Dictionary") '  index position de la clé dans TblD(): 1,2,3,..
  a = Range("A2:B" & [A65000].End(xlUp).Row)
  Dim TblD(): ReDim TblD(1 To UBound(a), 1 To 200)
  Set d2 = CreateObject("Scripting.Dictionary") ' position du dernier item de chaque clé
  For i = LBound(a) To UBound(a)
    If Not d1.exists(a(i, 1)) Then d1(a(i, 1)) = d1.Count + 1 ' index no ligne de la table TblD()
    d2(a(i, 1)) = d2(a(i, 1)) + 1 ' on incrémente la position dernier item de chaque clé
    TblD(d1(a(i, 1)), 1) = a(i, 1)
    TblD(d1(a(i, 1)), d2(a(i, 1)) + 1) = a(i, 2) ' item en ligne/colonne
  Next
  Range("d2").Resize(d1.Count, Application.Max(d2.items) + 1) = TblD
End Sub

ou

Sub Regroupe5()
  Set d1 = CreateObject("Scripting.Dictionary") ' clés et index des nos de ligne te TblD()
  Set d3 = CreateObject("Scripting.Dictionary") ' recherche max items de chaque clé
  a = Range("A2:B" & [A65000].End(xlUp).Row)
  For i = LBound(a) To UBound(a) ' Crée un index des positions des clés
    If Not d1.exists(a(i, 1)) Then d1(a(i, 1)) = d1.Count + 1 ' no ligne clé
    d3(a(i, 1)) = d3(a(i, 1)) + 1
  Next i
  mx = Application.Max(d3.items) ' maxi items
  Dim TblD(): ReDim TblD(1 To d1.Count, 1 To mx + 1)
  Set d2 = CreateObject("Scripting.Dictionary") ' position du dernier item de chaque clé
  For i = LBound(a) To UBound(a)
    d2(a(i, 1)) = d2(a(i, 1)) + 1
    TblD(d1(a(i, 1)), 1) = a(i, 1)
    TblD(d1(a(i, 1)), d2(a(i, 1)) + 1) = a(i, 2) ' item en ligne/colonne
  Next
  Range("d2").Resize(d1.Count, mx + 1) = TblD
End Sub

Autre exemple

Regroupe les codes dans une cellule

Regroupe

Sub Regroupe()
  Set d = CreateObject("Scripting.Dictionary")
  For Each c In Range("b2", [b65000].End(xlUp))
      d(c.Value) = d(c.Value) & c.Offset(0, -1) & " "
  Next c
  [d2].Resize(d.Count) = Application.Transpose(d.keys)
  [e2].Resize(d.Count) = Application.Transpose(d.items)
End Sub

Liste des valeurs uniques pour chaque code

Liste valeurs uniques par code

Sub DicoClassique()
  Set f = Sheets("bd")
  Tbl = f.[A1].CurrentRegion.Value
  NbLig = UBound(Tbl)
  Set dcode = CreateObject("scripting.dictionary")
  dcode.CompareMode = vbTextCompare ' ignore la casse
  For lig = 2 To NbLig ' remplissage dictionnaire
    dcode(Tbl(lig, 1)) = ""
  Next lig
  Set d = CreateObject("scripting.dictionary")
  d.CompareMode = vbTextCompare ' ignore la casse
  ligne = 1
  For Each code In dcode.keys
    ligne = ligne + 1
    d.RemoveAll
    For lig = 2 To NbLig ' remplissage dictionnaire
      If Tbl(lig, 1) = code Then d(Tbl(lig, 2)) = ""
    Next lig
    f.Cells(ligne, "e") = code
    f.Cells(ligne, "f").Resize(, d.Count) = d.keys
  Next code
End Sub

Autre méthode + rapide (0,25 sec pour 30.000 lignes)

Liste valeurs uniques par code rapide
Liste valeurs uniques par code rapide2

Sub RegroupeUniquesCode()
  Set f = Sheets("bd")
  Set d = CreateObject("Scripting.Dictionary")
  Set d1 = CreateObject("Scripting.Dictionary")
  Tbl = f.Range("A2:B" & f.[a65000].End(xlUp).Row).Value
  For i = LBound(Tbl) To UBound(Tbl) ' élimination doublons nuances
    If Tbl(i, 2) <> "" Then d1(Tbl(i, 1) & "|" & Tbl(i, 2)) = ""
  Next i
  For Each c In d1.keys ' regroupement par code
    a = Split(c, "|")
    d(a(0)) = d(a(0)) & a(1) & "|"
  Next c
  Set f2 = Sheets("résultat")
  Dim TblRes: ReDim TblRes(1 To d.Count, 1 To 2)
  i = 0
  For Each c In d.keys
    i = i + 1
   TblRes(i, 1) = c: TblRes(i, 2) = d(c)
  Next c
  f2.[A2].Resize(d.Count, 2) = TblRes
  Application.DisplayAlerts = False
  f2.[B2].Resize(d.Count).TextToColumns Other:=True, OtherChar:="|"
  f2.Cells.EntireRow.AutoFit
End Sub

Listes inverses

ListesInverses
ListesInverses2
ListesInverses3
ListesInverses 5

Sub ListeInverses()
  Set d = CreateObject("Scripting.Dictionary")
  For Each c In [B2:D2].Resize(Application.CountA([a:a]))
    If c.Value <> "" Then d(c.Value) = d(c.Value) & Cells(c.Row, 1) & " "
  Next c
  ligne = 2
  For Each c In d.keys
    Cells(ligne, "g") = c
    a = Split(d.Item(c), " ")
    Cells(ligne, "g").Offset(, 1).Resize(, UBound(a) + 1) = a
    ligne = ligne + 1
  Next c
  [G2].CurrentRegion.Sort Key1:=Range("G2"), Order1:=xlAscending, Header:=xlGuess
End Sub

Autre exemple

On a la liste des communes et codes postaux de chaque département sous forme d'une BD

Depart   Commune                              Codepos
AIN        AMAREINS                               1090
AIN        AMAREINS FRANCHELEINS CES 1090
AIN        AMBERIEU EN BUGEY                1500

On veut la liste des communes de chaque département sur un ligne

AIN      AMAREINS 1090 AMAREINS FRANCHELEINS CES 1090 AMBERIEU EN BUGEY 1500
AISNE  ABBECOURT 2300 ACHERY 2800 ACY 2200
ALLIER ABREST 3200 AGONGES 3210 AINAY LE CHATEAU 3360

Listes Inverses 3

Sub ListeInverses()
   Application.ScreenUpdating = False
   Set d = CreateObject("Scripting.Dictionary")
   Set f1 = Sheets("liste")
   a = f1.Range("a2:c" & f1.[A65000].End(xlUp).Row).Value
   For i = LBound(a) To UBound(a)
      d(a(i, 1)) = d(a(i, 1)) & "|" & a(i, 2) & "|" & a(i, 3)
   Next i
   ligne = 2
   Set f2 = Sheets("result")
   For Each c In d.Keys
      f2.Cells(ligne, "a") = c
      a = Split(d.Item(c), "|")
      f2.Cells(ligne, "a").Offset(, 1).Resize(, UBound(a) + 1) = a
      ligne = ligne + 1
   Next c
End Sub

Nombre de commandes distinctes par vendeur

Sub groupe2()
  Set mondico = CreateObject("Scripting.Dictionary")
  a = Range("b2:b" & [B65000].End(xlUp).Row).Value
  b = Range("a2:b" & [a65000].End(xlUp).Row).Value
  For Each c In a
    mondico(c) = 1
  Next
  [H2].Resize(mondico.Count) = Application.Transpose(mondico.keys)
  For Each v In mondico.keys
    Set mondico2 = CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(a)
      If a(i, 1) = v Then mondico2(b(i, 1)) = 1
    Next i
    [I2].Offset(k, 0) = mondico2.Count
    k = k + 1
  Next v
End Sub

ADOGroupBY

Extraction des listes par catégorie

ExtractCaté

Private Sub Worksheet_Activate()
  Set f = Sheets("référence")
  [A2:H10000].ClearContents
  For d = 0 To 6
    Set mondico = CreateObject("Scripting.Dictionary")
    For Each c In f.Range(f.Cells(2, 2 + d), f.Cells(65000, 2 + d).End(xlUp))
      If c = "x" Then mondico(c.Offset(, -1 - d).Value) = 1
    Next c
    Cells(2, 2 + d).Resize(mondico.Count, 1) = Application.Transpose(mondico.keys)
    Range(Cells(2, 2 + d), Cells(1000, 3 + d)).Sort Key1:=Cells(2, 2 + d), Order1:=xlAscending, Header:=xlNo
   Next d
   f.[A2:A10000].Copy [A2]
   [A1:A10000].Sort Key1:=Range("A2"), Order1:=xlAscending, Header:=xlGuess
End Sub

Comparaison de classeurs (10.000 éléments)

On veut connaitre les éléments de Classeur1.xls qui n'existent pas dans Classeur2.xls

Sub ComparaisonColonne()
  f = 1 'no feuille
  Application.ScreenUpdating = False
  Set MonDico1 = CreateObject("Scripting.Dictionary")
  Set MonDico2 = CreateObject("Scripting.Dictionary")
  Workbooks("classeur1.xls").Activate
  Sheets(f).Activate
  For Each c In Sheets(f).Range("A:D").SpecialCells(xlCellTypeConstants, 23)
     MonDico1(c.Value) = c.Address
  Next
  Workbooks("classeur2.xls").Activate
  Sheets(f).Activate
  For Each c In Sheets(f).Range("A:D").SpecialCells(xlCellTypeConstants, 23)
    MonDico2(c.Value) = ""
  Next
  Workbooks("classeur1.xls").Activate
  For Each e In MonDico1
    Range(MonDico1(e)).Font.Color = IIf(MonDico2.Exists(e), vbBlack, vbRed)
  Next
  Application.ScreenUpdating = True
End Sub

Liste des mots d'un champ

La fonction matricielle ListeMots() extrait tous les mots d'un champ.

ListeMots


Function ListeMots(champ As Range)
  exclus = Array("le", "les", "des", "sur", "elle", "est")
  a = champ
  Set mondico = CreateObject("Scripting.Dictionary")
  For Each c In a
     b = Split(c, " ")
     For Each k In b
        If Len(k) > 2 And Not IsNumeric(k) And IsError(Application.Match(k, exclus, 0)) Then
           mondico.Item(LCase(k)) = LCase(k)
        End If
     Next k
  Next c
  Dim temp()
  ReDim temp(1 To Application.Caller.Rows.Count)
  i = 1
  For Each c In mondico.items
    temp(i) = c
    i = i + 1
  Next
  Call tri(temp, 1, mondico.Count)
  ListeMots = Application.Transpose(temp)
End Function

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
temp = a(g): a(g) = a(d): a(d) = temp
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

Nos complémentaires

On recherche les nos complémentaires d'une liste de nos.

Complément
Complément3

Sub Mcomplément()
  Set mondico = CreateObject("Scripting.Dictionary")
  Set champ = Range("A2:A1000")
  For i = Application.Min(champ) To Application.Max(champ)
    If IsError(Application.Match(i, champ, 0)) Then mondico(i) = i
  Next i
  [C2].Resize(mondico.Count) = Application.Transpose(mondico.items)
End Sub

Sous forme d'une fonction perso matricielle.

Function complément(champ)
  Set mondico = CreateObject("Scripting.Dictionary")
  For i = Application.Min(champ) To Application.Max(champ)
    If IsError(Application.Match(i, champ, 0)) Then mondico(i) = i
  Next i
  Dim b()
  ReDim b(1 To champ.Count)
  i = 1
  For Each c In mondico.items
    b(i) = c
    i = i + 1
  Next
  complément = Application.Transpose(b)
End Function

Fonction liste sans doublons triée multi-zones

ListeSDTriéeMZ
FonctionSansDoublonsTriéeMultiZones

Function ListeSDTriéeMZ(champ)
  Application.Volatile
  Set mondico = CreateObject("Scripting.Dictionary")
  For i = 1 To champ.Areas.Count ' parcours des zones du champ multi-zones
     For j = 1 To champ.Areas(i).Count ' parcours des éléments d'une zone
       If champ.Areas(i)(j) <> "" And champ.Areas(i)(j) <> "." Then
         temp = champ.Areas(i)(j)
         mondico.Item(temp) = temp ' ajout au dictionnaire (doublons éliminés)
       End If
     Next j
   Next i
   temp = mondico.items 'transfert dictionnaire dans une table temp()
   Call Tri(temp, LBound(temp), UBound(temp)) ' tri optionnel
   Dim d(): ReDim d(Application.Caller.Rows.Count) ' table pour retour
   For i = LBound(temp) To UBound(temp): d(i) = temp(i): Next i
   ListeSDTriéeMZ = Application.Transpose(d)
End Function

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

Suppression de doublons multi-feuilles

SupDoublonsMF2Critères
SupDoublonsMFUnCritère

Sub ListeSansDoublons()
  Set mondico = CreateObject("Scripting.Dictionary")
    For s = 1 To Sheets.Count - 1
      For Each c In Range(Sheets(s).[a2], Sheets(s).[a65000].End(xlUp))
         tmp = c & "*" & c.Offset(, 2)
         mondico(tmp) = tmp
      Next c
   Next s
   i = 2
   For Each c In mondico
    a = Split(c, "*")
    Sheets("synthèse").Cells(i, 1) = a(0)
    Sheets("synthèse").Cells(i, 2) = "'" & (a(1))
    i = i + 1
  Next c
End Sub

Sub ColoriageDoublons()
  Set mondico = CreateObject("Scripting.Dictionary")
  For s = 1 To Sheets.Count - 1
     [A:A].Interior.ColorIndex = xlNone
     For Each c In Range(Sheets(s).[a2], Sheets(s).[a65000].End(xlUp))
        tmp = c & "*" & c.Offset(, 2)
        mondico.Item(tmp) = mondico.Item(tmp) + 1
    Next c
  Next s
  For s = 1 To Sheets.Count - 1
    For Each c In Range(Sheets(s).[a2], Sheets(s).[a65000].End(xlUp))
       tmp = c & "*" & c.Offset(, 2)
       If mondico.Item(tmp) > 1 Then c.Interior.ColorIndex = 3
     Next c
   Next s
End Sub

Concaténation d'un champ

On a dans un champ une liste avec des cellules vides:

aa
bb
cc
dd

On veut obtenir aa,bb,cc,dd

=ConcatChamp(A2:A100;",")

Function concatChamp(champ As Range, sep)
  Set mondico = CreateObject("Scripting.Dictionary")
  For i = 1 To champ.Count
    If Not IsEmpty(champ(i)) And Not champ(i) = 0 Then mondico(champ(i).Value) = ""
  Next i
  concatChamp = Join(mondico.keys, sep)
End Function

Pour obtenir aa,bb,cc et dd

Function concatChamp2(champ As Range, sep)
  Set mondico = CreateObject("Scripting.Dictionary")
  For i = 1 To champ.Count
    If Not IsEmpty(champ(i)) And Not champ(i) = 0 Then mondico(champ(i)) = 1
  Next i
  temp = Join(mondico.keys, sep)
  p = InStrRev(temp, ",")
  concatChamp2 = Left(temp, p - 1) & Replace(Mid(temp, p), ",", " et ")
End Function

Concat

Mise à jour d'une liste existante

En colonne A de la feuille Extraction sans doublons, nous avons la liste des numéros appelés dans l'année.
Périodiquement, on ajoute les nouveaux numéros appelés de la feuille Appels du mois . Les nouveaux numéros sont ajoutés à la fin de la liste.

MajListeExistante

Sub MajTph()
  Set f1 = Sheets("appels du mois")
  Set f2 = Sheets("extraction sans doublons")
  Set d1 = CreateObject("Scripting.Dictionary")
  For Each c In f2.[A2:A1000]
    If c.Text <> "" Then d1(c.Text) = ""
  Next c
  Set d2 = CreateObject("Scripting.Dictionary")
  For Each c In f1.[A3:L35]
    If c.Text <> "" Then
      If Not d1.exists(c.Text) Then d2(c.Text) = ""
    End If
  Next c
  If d2.Count > 0 Then f2.[a65000].End(xlUp).Offset(1).Resize(d2.Count, 1) = Application.Transpose(d2.keys)
End Sub

Recherche rapide dans une colonne d'un tableau 2D

Une recherche d'une valeur dans une colonne d'un tableau 2D de 20.000 lignes est égale à 0,008 sec.
Pour accélérer la recherche d'une clé dans une colonne d'un tableau 2D, on peut l'indexer par un Dictionnaire (idée de Pierre Jean). On obtient un temps de 0,00003 sec (rapport 200).

IndexationTableau2DDico

Sub RechercheDico()
  '---- Création de l'index
  Set mondico = CreateObject("scripting.dictionary")
  a = [A1:C20000]
  For i = 1 To 20000
     mondico(a(i, 1)) = i
  Next i
  '--- Recherche
  clé = "Nom15000"
  ligne = mondico(clé)
  val1 = a(ligne, 2)
  val2 = a(ligne, 3)
  MsgBox val1 & " " & val2
End Sub

ou

Sub RechercheDico2()
  Set mondico = CreateObject("scripting.dictionary")
  a = [A1:C20000]
  For i = 1 To 20000
     mondico(a(i, 1)) = i
  Next i
  clé = "Nom15000"
  ligne = mondico(clé)
  b = Application.Index(a, ligne)
  MsgBox b(1) & " " & b(2) & " " & b(3)
End Sub

Un dictionnaire n'accepte que des clés uniques.
Si la colonne ne contient pas que des clés uniques (la ville par exemple en colonne D contient plusieurs fois Paris), on fabrique alors des pseudos clés.

Sub RechercheDicoVille()
  '--- construction index ville (plusieurs fois la même ville)
  Set mondico = CreateObject("scripting.dictionary")
  a = [A1:D20000]
  For i = 1 To 20000
    CléBase = a(i, 4)
    Clé = CléBase
    indice = 1
    Do While mondico.exists(Clé)
       Clé = CléBase & indice
       indice = indice + 1
    Loop
    mondico(Clé) = i
  Next i
  '--recherche (0,03 sec pour 1.000 recherches)
  CléBase = "Paris"
  Clé = CléBase
  indice = 1
  Do While mondico.exists(Clé)
    ligne = mondico(Clé)
    val1 = a(ligne, 1)
    val2 = a(ligne, 4)
    MsgBox val1 & " " & val2
    Clé = CléBase & indice
    indice = indice + 1
  Loop
End Sub

Meilleure note

Donne le produit qui contient le plus de mots par rapport à la demande client.

Meilleure Note
Meilleure Note2

Recherche d'une valeur proche

Nous recherchons Entr. de recup. dans Entreprise de récupération

Proche
Proche3
ProcheMult
ProcheMult2
ProcheMult3
Proche Société

Synthèse de 2 tableaux

SynthèsexTableaux

Suppression doublons colonne

Suppression doublons en colonnes

Syntheses 3D

Cette fonction perso matricielle calcule la somme de plusieurs onglets suivant 2 critères.
-Les listes des codes et des villes sont obtenues et triées automatiquement par la fonction.
-Cette fonction est rapide: grâce à Dictionary, la recherche de la ligne et de la colonne du tableau de cumul Tbl() se fait très rapidement.

Fonction Somme3D 2 critères
Fonction Somme3D 2 critères2
Fonction Somme3D 2 critères MAC
Fonction Somme3D 1 col Num
Fonction Somme3D N col Num
Fonction Somme3D N col Num Index
Fonction Somme3D N col Num MAC
Fonction Somme3D N col Num IndexMAC

-Sélectionner A1:E10
=S3DTriée(1;3; "a2:a20";"b2:b20"; "c2:c20")
-valider avec maj+ctrl+entrée

Function S3DTriée(début, fin, critLigne, CritColonne, ChampSomme)
  Application.Volatile
  Dim Tbl()
  ReDim Tbl(0 To Application.Caller.Rows.Count, 0 To Application.Caller.Columns.Count)
  Set dLig = CreateObject("Scripting.Dictionary")
  Set dCol = CreateObject("Scripting.Dictionary")
  For s = début To fin
    a = Sheets(s).Range(critLigne).Value
    b = Sheets(s).Range(CritColonne).Value
    For i = LBound(a) To UBound(a)
      If a(i, 1) <> "" Then If Not dLig.exists(a(i, 1)) Then dLig(a(i, 1)) = ""
      If b(i, 1) <> "" Then If Not dCol.exists(b(i, 1)) Then dCol(b(i, 1)) = ""
    Next i
  Next s
  crit1 = dLig.keys: Call Tri(crit1, LBound(crit1), UBound(crit1))
  dLig.RemoveAll: For i = 0 To UBound(crit1): dLig(crit1(i)) = i + 1: Next
  lig = 1: For Each c In dLig.keys: Tbl(lig, 0) = c: lig = lig + 1: Next c
  crit1 = dCol.keys: Call Tri(crit1, LBound(crit1), UBound(crit1))
  dCol.RemoveAll: For i = 0 To UBound(crit1): dCol(crit1(i)) = i + 1: Next
  k = 1: For Each c In dCol.keys: Tbl(0, k) = c: k = k + 1: Next c
  For s = début To fin
    idxLig = Sheets(s).Range(critLigne).Value
    idxCol = Sheets(s).Range(CritColonne).Value
    a = Sheets(s).Range(ChampSomme).Value
    For lig = LBound(a) To UBound(a)
      cléLig = CStr(idxLig(lig, 1)): clécol = idxCol(lig, 1)
      If cléLig <> "" And clécol <> "" Then
        ligtbl = dLig(cléLig): coltbl = dCol(clécol)
        Tbl(ligtbl, coltbl) = Tbl(ligtbl, coltbl) + a(lig, 1)
      End If
    Next lig
  Next s
  S3DTriée = Tbl
End Function

Remplacer par multiple

On doit remplacer les contenus de cellules mal orthographiés.
La corrrespondance Mauvaise orthographe -> Bonne orthographe des mots à remplacer est dans un dictionnaire.La recherche dans ce dictionnaire se fait très rapidement.

Remplacer par

Sub essai()
  Application.ScreenUpdating = False
  Set d = CreateObject("Scripting.Dictionary")
  a = [H2:I2].Resize([h65000].End(xlUp).Row)
  For i = LBound(a) To UBound(a)
     d(a(i, 1)) = a(i, 2)
  Next i
  For Each c In Selection
     If d.exists(c.Value) Then c.Value = d(c.Value)
  Next
End Sub

Recherche rapide de mots dans des phrases

On recherche dans des phrases en colonne A la présence de mots en colonne C.

Dico Phrase Mots
Fonction Recherche Mots Phrases

Sub rechercheMotEntiersDansPhrase()
  Application.ScreenUpdating = False
  Set d = CreateObject("Scripting.Dictionary")
  For Each c In Range("D2:D" & [D65000].End(xlUp).Row): d(c.Value) = "": Next c
    Tbl = Range("a2:a" & [A65000].End(xlUp).Row).Value
     ReDim TblResult(1 To UBound(Tbl))
     For i = LBound(Tbl) To UBound(Tbl)
        b = Split(Replace(Tbl(i, 1), "'", " "), " ")
        For j = LBound(b) To UBound(b)
          If d.Exists(b(j)) Then TblResult(i) = TblResult(i) & " " & b(j)
        Next j
     Next i
     [b2].Resize(UBound(Tbl)) = Application.Transpose(TblResult)
End Sub

Indexation d'une BD pour une recherche rapide de mots

Sur cette version, nous recherchons des mots dans une BD de 30.000 phrases.
Un index de 3000 mots vers les phrases est crée à l'aide d'un dictionnaire et sauvegardé dans le classeur.Le temps de recherche d'un mot est <0,01 sec.

Recherche rapide phrases qui contiennent un mot
Recherche rapide phrases qui contiennent 2 mots

Ci dessous, pour une recherche de rue dans une liste de 330.000 adresses, le temps de recherche est inférieur à 0,1 sec. La recherche dans le combobox est intuitive.

-Avec les Arrays ou le Filtre élaboré, le temps de recherche est proportionnel au nombre de lignes de la BD.
-Avec l'indexation des mots (colonnes F & G) , le temps de recherche est très faible et augmente très peu avec le nombre de lignes de la BD. Ce qui prend du temps, c'est la création de l'index( à faire une seule fois). Dans le fichier joint, elle est déjà faite (Colonnes F et G). Dans le fichier joint (330.000 lignes) , on peut comparer les temps des différentes méthodes (onglet tests)

Recherche rapide d'une rue dans une adresse
Recherche rapide d'une rue dans une adresse 2
Recherche Multi mots

Option Compare Text
Dim bd(), choix1(), Choix1Col()
Private Sub UserForm_Initialize()
  Set d = CreateObject("Scripting.Dictionary")
  bd = Range("f2:g" & [f65000].End(xlUp).Row).Value
  For i = 1 To UBound(bd)
     d(bd(i, 1)) = bd(i, 2)
  Next i
  choix1 = bd
  Me.ComboBox1.List = bd
End Sub

Private Sub ComboBox1_Change()
   If Me.ComboBox1.ListIndex = -1 Then
      Dim b()
      tmp = Me.ComboBox1 & "*"
      n = 0
      For i = LBound(choix1) To UBound(choix1)
        If UCase(choix1(i, 1)) Like tmp Then
           n = n + 1: ReDim Preserve b(1 To 2, 1 To n)
           b(1, n) = choix1(i, 1): b(2, n) = choix1(i, 2)
       End If
     Next i
     If n > 0 Then Me.ComboBox1.Column = b: Me.ComboBox1.DropDown
   End If
  End If
End Sub

Private Sub ComboBox1_Click()
  Me.ListBox1.Clear
  If Me.ComboBox1.Column(1) <> "" Then
    b = Split(Me.ComboBox1.Column(1), "|")
    n = 0
    Dim bb()
    For Each c In b
      If c <> "" Then
         n = n + 1: ReDim Preserve bb(1 To 2, 1 To n)
         bb(1, n) = Cells(Val(c), "A"): bb(2, n) = c
       End If
    Next c
    Me.ListBox1.Column = bb
    Me.TextBox1 = Me.ListBox1.ListCount
  End If
End Sub

Recherche Array classique

Sub RechercheArrayClassique()
  t = Timer
  mot = "*maupassant*"
  Tbl = Range("a2:a" & [a1000000].End(xlUp).Row)
  n = 0: Dim b()
  For i = 1 To UBound(Tbl)
    If Tbl(i, 1) Like mot Then
      n = n + 1: ReDim Preserve b(1 To n)
      b(n) = Tbl(i, 1)
    End If
  Next i
  [C2].Resize(n) = Application.Transpose(b)
  MsgBox Timer - t     ' 1,4 sec pour 330.000 lignes
  MsgBox n
End Sub

Recherche Array avec Filter()

Sub RechercheFilter()
  t = Timer
  mot = "maupassant"
  Tbl = Range("a2:a" & [a1000000].End(xlUp).Row)
  n = UBound(Tbl)
  Dim a(): ReDim a(1 To n)
  For i = 1 To n
    a(i) = Tbl(i, 1)
  Next i
  b = Filter(a, mot, True, vbTextCompare)
  [C2].Resize(n) = Application.Transpose(b)
  MsgBox Timer - t    ' 1,6 sec pour 330.000 lignes
End Sub

Filtre avancé

Sub FiltreAvancé()
  tt = Timer
  Set f = Sheets("bd")
  f.Range("A1:B333515").AdvancedFilter Action:=xlFilterCopy, CriteriaRange:= _
     Range("h1:h2"), CopyToRange:=Range("j1:k1"), Unique:=False
  MsgBox Timer - tt  ' 2,5 sec pour 330.000 lignes
End Sub

Fonction FrequenceTexte

Donne la fréquence de texte dans une liste de cellules contenant du texte.

Fonction_Frequence_Texte
Fonction_Frequence_Texte_Mac
Fonction_Frequence_Mot_Mac
Fonction Frequence Texte Classement
Fonction Frequence Mots
Fonction Frequence Texte critère
Fonction Frequence Texte Groupe

Function FrequenceTexte(champ As Range)
   Set d1 = CreateObject("Scripting.Dictionary")
   d1.CompareMode = vbTextCompare
   temp = champ
   For i = LBound(temp) To UBound(temp)
     c = temp(i, 1)
     If c <> "" Then d1(c) = d1(c) + 1
   Next i
   Dim b()
   ReDim b(1 To d1.Count, 1 To 2)
   i = 1
   For Each c In d1.keys
     b(i, 1) = c: b(i, 2) = d1(c)
   i = i + 1
   Next
   Call tri(b, 1, d1.Count)
   FrequenceTexte = b
End Function

Nombre d'occurences des doublons

On veut classer les doublons par ordre du nombre d'occurences.

Occurences doublons

Sub DoublonsOccurence()
  a = Range("A2:H" & [A65000].End(xlUp).Row).Value
  Set d = CreateObject("scripting.dictionary")
  For i = LBound(a) To UBound(a)
     d(CStr(a(i, 2))) = d(CStr(a(i, 2))) + 1
     a(i, UBound(a, 2)) = d(CStr(a(i, 2)))
  Next i
  Set f = Sheets("result")
  f.Cells.Clear: [A1:H1].Copy f.[A1]
  f.[a2].Resize(UBound(a), UBound(a, 2)) = a
  f.[a2].Sort key1:=f.[h2], key2:=f.[b2], Header:=yes
  For i = f.[A65000].End(xlUp).Row To 3 Step -1
     If f.Cells(i, 8) <> f.Cells(i - 1, 8) Then f.Rows(i).Insert
  Next i
  Set Rng = f.Range("A2:H" & f.[A65000].End(xlUp).Row).SpecialCells(xlCellTypeConstants)
  For i = 1 To Rng.Areas.Count
    Rng.Areas(i).BorderAround Weight:=xlMedium
  Next i
End Sub

Fonction communs à 3 listes

-sélectionner W2:Z2
=communs(B2:H2;J2:O2;Q2:U2)
Valider avec maj+ctlrl +entrée

Fonction Communs 3 listes

Avantages d'une fonction UDF
-Utilisable comme une fonction standard par une personne qui ne connait pas VBA
-Pas besoin de modifier le code si on déplace les données
-Réutilisable

Function Communs(tab1, tab2, tab3)
  Dim temp()
  Set d1 = CreateObject("Scripting.Dictionary")
  For Each c In tab1
    If Not d1.Exists(c.Value) Then d1(c.Value) = ""
  Next c
  Set d2 = CreateObject("Scripting.Dictionary")
  For Each c In tab2
     If c.Value <> "" And d1.Exists(c.Value) Then
        If Not d2.Exists(c.Value) Then d2(c.Value) = ""
     End If
   Next c
   Set d3 = CreateObject("Scripting.Dictionary")
   For Each c In tab3
      If c <> "" And d2.Exists(c.Value) Then
         If Not d3.Exists(c.Value) Then d3(c.Value) = ""
      End If
    Next c
    n = Application.Caller.Columns.Count
    If n < d3.Count Then
       Communs = "Selection insuffisante"
    Else
      ReDim temp(1 To n)
      i = 1
     For Each c In d3.keys
        temp(i) = c
        i = i + 1
     Next
     Communs = temp
   End If
End Function

Extraction de lignes d'un Array

Sur cet exemple, nous extrayons les lignes d'un Array pour chaque code.

Extraction Array
Extraction Array Onglets

-Nous créons un dictionnaire d des codes. Pour chaque code, nous stockons les nos de lignes du tableau(Array)

Set d = CreateObject("scripting.dictionary")
For i = 1 To UBound(bd): d(bd(i, 2)) = d(bd(i, 2)) & i & ",": Next ' Dictionnaire

-Pour chaque code, nous extrayons les lignes du tableau bd() dans un tableau a()

For Each k In d.keys
   a = Application.Index(bd, Application.Transpose(Split(d.Item(k), ",")), Array(1, 3)) 'Extract Array
   f.Cells(ligne + 1, "g").Resize(UBound(a) - 1, UBound(a, 2)) = a
   ligne = ligne + UBound(a) + 1
Next k

Liste des items pour chaque code

Pour chaque code, on veut la liste des items

Code     Item
1000003 AC-026
1000003 AC-051

1000004 AC-027
1000004 AC-052
1000004 AC-053

  =>

Code       Items
1000003   AC-026,AC-051
1000004   AC-027,AC-052,AC-053

Sub ListeItemsi()
  Set Rng = Range("A2:A" & [A65000].End(xlUp).Row)
  Set d = CreateObject("Scripting.Dictionary")
  For Each c In Rng
     If d.exists(c.Value) Then d(c.Value) = d(c.Value) & "," & c.Offset(, 1).Value Else d(c.Value) = c.Offset(, 1).Value
   Next
   [E2].Resize(d.Count) = Application.Transpose(d.keys)
   [f2].Resize(d.Count) = Application.Transpose(d.items)
End Sub

Fonction perso NBSIMAT(champ) plus rapide que NB.SI()

Dans la colonne A, on a une liste de noms (10.000 noms). On veut compter en colonne B le nombre de fois que ces noms apparaissent avec NB.SI(A$2:A$10000;A2).

- En recopiant cette formule 10.000 fois, le temps de calcul est de 3 secondes
-Avec une fonction perso NBSIMAT(A2:A10000), le temps de calcul n'est pas visuellement mesurable

NBSIMAT

Function NBSIMAT(champ)
  t = champ
  Set d1 = CreateObject("Scripting.Dictionary")
  For Each c In t
     d1(c) = d1(c) + 1
  Next c
  Dim t2(): ReDim t2(1 To UBound(t))
  For i = 1 To UBound(t)
     t2(i) = d1(t(i, 1))
  Next i
  NBSIMAT = Application.Transpose(t2)
End Function

La fonction perso =FiltreCol(champColBD;TitreCol) donne l’expression d'un filtre automatique pour la colonne

Filtre Auto Fonctions Perso

Function FiltreCol(Champ As Range, TitreChamp As Range)
  Application.Volatile
  If Not ChampActif(TitreChamp) Then FiltreCol = "": Exit Function
  Set d = CreateObject("scripting.dictionary")
  d.CompareMode = vbTextCompare
  For Each c In Champ
     If Not c.EntireRow.Hidden And c.Value <> "" Then d(c.Value) = c.Value
  Next c
  a = d.items
  If IsDate(Champ(1)) Then
    If d.Count = 1 Then
       FiltreCol = TitreChamp & ":" & Format(a(0), "dd/mm/yyyy")
    Else
       mini = a(0): maxi = a(0)
       For i = LBound(a) To UBound(a)
         If a(i) < mini Then mini = a(i)
         If a(i) > maxi Then maxi = a(i)
       Next i
       FiltreCol = TitreChamp & ":" & "> " & mini & " et < " & maxi
    End If
   Else
     FiltreCol = TitreChamp & ":" & Join(a, ",")
   End If
End Function

Fonction nombre valeurs uniques avec 1 ou 2 critères

Nb Valeurs Uniques 1 critère

Sub NbUniques()
  Set f = Sheets("bd")
  Tbl = f.Range("A2:B" & f.[A65000].End(xlUp).Row).Value
  Set d = CreateObject("scripting.dictionary") ' dictionnaire des critères
  Set d2 = CreateObject("scripting.dictionary")
  For i = 1 To UBound(Tbl)
   If Not d.exists(Tbl(i, 2)) Then d(Tbl(i, 2)) = 0
    tmp = Tbl(i, 2) & "|" & Tbl(i, 1)
    If Not d2.exists(tmp) Then d2(tmp) = "": d(Tbl(i, 2)) = d(Tbl(i, 2)) + 1
  Next i
  Dim TblS(): ReDim TblS(1 To d.Count, 1 To 2)
  [E2].Resize(d.Count) = Application.Transpose(d.Keys)
  [F2].Resize(d.Count) = Application.Transpose(d.items)
  [E2].Resize(d.Count, 2).Sort key1:=Range("e2"), order1:=xlAscending, Header:=xlNo
End Sub

Version avec un tableau de dictionnaires

Sub NbUniques2()
  Set f = Sheets("bd")
  Tbl = f.Range("A2:B" & f.[A65000].End(xlUp).Row).Value
  Set d = CreateObject("scripting.dictionary") 'critères
  For i = 1 To UBound(Tbl): d(Tbl(i, 2)) = "": Next i 'dictionnaire des critères
  k = 0: For Each c In d.Keys: k = k + 1: d(c) = k: Next c 'indexation des dictionnaires
  Dim TblDict(): ReDim TblDict(1 To d.Count)
  For i = 1 To UBound(TblDict)
    Set TblDict(i) = CreateObject("scripting.dictionary")
  Next i
  For i = 1 To UBound(Tbl)
    NoDico = d(Tbl(i, 2))
    TblDict(NoDico)(Tbl(i, 1)) = ""
  Next i
  For i = 1 To UBound(TblDict): TblDict(i) = TblDict(i).Count: Next i
  [E2].Resize(d.Count) = Application.Transpose(d.Keys)
  [F2].Resize(d.Count) = Application.Transpose(TblDict)
  [E2].Resize(d.Count, 2).Sort key1:=Range("e2"), order1:=xlAscending, Header:=xlNo
End Sub

Sous forme de fonctions persos

Fonction N b Valeurs Uniques 1 critère
Fonction N b Valeurs Uniques 1 critère 2
Fonction N b Valeurs Uniques 1 critère 3
Fonction Nb Valeurs Uniques 1 critère MAC

Function NbValUniques(Valeurs As Range)
  Application.Volatile
  TblVal = Valeurs
  Set d = CreateObject("scripting.dictionary")
  For i = 1 To UBound(TblVal)
    If Not d.exists(TblVal(i, 1)) Then d(TblVal(i, 1)) = d(TblVal(i, 1)) + 1
  Next i
  NbValUniques = d.Count
End Function

Function NbValUniques1crit(Valeurs As Range, Critère1 As Range, crit As String)
  Application.Volatile
  TblVal = Valeurs
  Tcrit1 = Critère1
  critere = crit
  Set d = CreateObject("scripting.dictionary")
  For i = 1 To UBound(TblVal)
    If Tcrit1(i, 1) = crit Then
       If Not d.exists(TblVal(i, 1)) Then d(TblVal(i, 1)) = d(TblVal(i, 1)) + 1
    End If
  Next i
  NbValUniques1crit = d.Count
End Function

Nb Valeurs Uniques 2 critères
Fonction Nb Valeurs Uniques 2 critères
Fonction Nb Valeurs Uniques 2 critères croisé
Fonction Nb Valeurs Uniques 2 critères MAC

Compte les noms d'un champ

Compte le nombre de noms d'un champ. On peut définir l'ordre croissant ou décroissant.

Compte Noms Champ
Compte Noms Champ PC & MAC
Compte Noms Champ 3D PC & MAC

-sélectionner D2:E40
=comptenoms(B2:B20;2;2)
-Valider avec maj+ctrl+entrée

Function CompteNoms(champ As Range, ColTri, Ordre)
  Set d = CreateObject("scripting.dictionary")
  For Each c In champ
    a = Split(c, "-")
    For i = LBound(a) To UBound(a)
      tmp = Trim(a(i))
      d(tmp) = d(tmp) + 1
    Next i
  Next c
  i = 0
  n = Application.Caller.Rows.Count
  ReDim b(1 To n, 1 To 2)
  For Each c In d.keys
    i = i + 1
    b(i, 1) = c: b(i, 2) = d(c)
  Next c
  TriD2col b, LBound(b), d.Count, ColTri, Ordre
  CompteNoms = b
End Function

Rapport

On veut regrouper les dates (colonne A) par Séance (colonnes B et C) pour chaque adhérent.

Rapport

Sub RapportDic()
  Application.ScreenUpdating = False
  Set f1 = Sheets("planning")
  Set AdrResult = Sheets("Rapport").Range("A1")
  TblE = f1.Range("A2").CurrentRegion.Value2      ' Table entrée
  For Adh = 1 To Ubound(TblE,2)-1
    n = 0: i = 2
   Set d = CreateObject("scripting.dictionary")
   For i = 2 To UBound(TblE)
     clé = TblE(i, Adh + 1)
     If d.exists(clé) Then d(clé) = d(clé) & "|" & TblE(i, 1) Else d(clé) = TblE(i, 1)
   Next i
   AdrResult.Offset(, (Adh - 1) * 3) = TblE(1, Adh + 1)
   For Each clé In d
     AdrResult.Offset(1 + n, (Adh - 1) * 3) = clé
     AdrResult.Offset(1 + n, (Adh - 1) * 3).Interior.ColorIndex = 4
     Tbl = Split(d(clé), "|")
     AdrResult.Offset(n + 1, (Adh - 1) * 3 + 1).Resize(UBound(Tbl) + 1) = Application.Transpose(Tbl)
     AdrResult.Offset(n + 1, (Adh - 1) * 3 + 1).Resize(UBound(Tbl) + 1).NumberFormat = "dd/mm/yyyy"
     AdrResult.Offset(1 + n, (Adh - 1) * 3 + 1).Resize(UBound(Tbl) + 1).BorderAround Weight:=xlThin
     n = n + UBound(Tbl) + 2
   Next clé
  Next Adh
End Sub

Dictionary pour remplacer Sommeprod()

Comment améliorer Sommeprod() lorsque cette fonction

- travaille sur des champs de taille importante
- est recopiée x1000 fois

Sur l'exemple en PJ, avec une fonction perso matricielle, on passe d'un temps de recalcul de 3 sec à 0,05 sec pour 4.000 lignes

=SOMMEPROD((dates=A2)*(numero=B2)) ou =CombienFois(numero; dates)

CombienPerso
CombienSommeProd

Sur l'exemple ci dessous, pour une BD de 60.000 lignes et 2x400 formules,
On obtient un temps de recalcul de 1,2 sec contre 15 sec avec Sommeprod()

MatricielPerso
SommeSi 1 critère
SommeSi 2 critères
SommeSi 3 critères

Somme d'une BD par catégorie

On veut la somme du CA pour le secteur primaire (secteurs A,B,C)

=SOMME(SI(ESTNUM(EQUIV(Secteur;SecteurPri;0));CAHT))

SommeBDClasse
SommeBDClasse2

Maj colonne avec Dictionary (0,17 sec) au lieu VLookUp (7 secondes)

On a une table de 20.000 items.
On veut mettre à jour une colonne de 2700 items référençant cette table

Maj VlookUp

Sub RechvM2()
  Application.ScreenUpdating = False
  Set clé = Range("F2:F2673") ' valeurs cherchées
  Set résult = Range("G2:G2673")
  colResult = 2
  For i = 1 To clé.Count
    tmp = clé.Cells(i, 1)
    résult.Cells(i, 1) = Application.VLookup(tmp, [matable], colResult, False)
  Next i
End Sub

avec un dictionnaire (0,17 seconde)

Sub RechvM()
  Set clé = Range("F2:F2673") ' valeurs cherchées
  Set résult = Range("G2:G2673")
  colResult = 2
  messageErreur = "inconnu"
  Set d = CreateObject("Scripting.Dictionary")
  a = [matable].Value
  b = clé.Value
  For i = LBound(a) To UBound(a)
    d(a(i, 1)) = a(i, colResult)
  Next i
  Dim temp(): ReDim temp(LBound(b) To UBound(b), 1 To 1)
  For i = LBound(b) To UBound(b)
     If d(b(i, 1)) <> "" Then temp(i, 1) = d(b(i, 1)) Else temp(i, 1) = messageErreur
  Next i
  résult.Value = temp
End Sub

Fusion de 2 Arrays une dimension

Fusion 2 Arrays

Sub EssaiFusionTBl()
  Set f = Sheets("feuil1")
  Tbl1 = Application.Transpose(f.Range("a2:a" & f.[A65000].End(xlUp).Row))
  Tbl2 = Application.Transpose(f.Range("b2:b" & f.[b65000].End(xlUp).Row))
  Tbl3 = FusionTbl(Tbl1, Tbl2)
'[D2].Resize(UBound(Tbl3)) = Application.Transpose(Tbl3)
End Sub

Function FusionTbl(a, b)
  Set d = CreateObject("scripting.dictionary")
  For i = 1 To UBound(a): d(a(i)) = "": Next i
  For i = 1 To UBound(b): d(b(i)) = "": Next i
  FusionTbl = d.keys
End Function

Recopie rapide de lignes d'un champ

On veut extraire certaines lignes d'un champ d'une feuille pour les recopier sur une autre feuille.

Recopie rapide de lignes d'un champ

Sub copieLignes()
  Set f = Sheets("Tableau_1")
  TblE = f.Range("A2:O" & f.[A65000].End(xlUp).Row).Value
  Set d = CreateObject("scripting.dictionary")
  For i = 1 To UBound(TblE)
    clé = TblE(i, 1) & "|" & TblE(i, 3) & "|" & TblE(i, 4) & "|" & TblE(i, 5)
    d(clé) = i ' index
  Next i
  Set f = Sheets("Tableau_2")
  TblS = f.Range("A2:D" & f.[A65000].End(xlUp).Row).Value
  Dim TblS2(): ReDim TblS2(1 To UBound(TblS), 1 To 7)
  Ncol = UBound(TblS, 2)
  For i = 1 To UBound(TblS)
    clé = TblS(i, 1) & "|" & TblS(i, 2) & "|" & TblS(i, 3) & "|" & TblS(i, 4)
    ligne = d(clé)
    For k = 1 To 7
      TblS2(i, k) = TblE(ligne, k + 8)
    Next k
  Next i
  f.[E2].Resize(UBound(TblS2), UBound(TblS2, 2)) = TblS2
End Sub

Synthèse de données

On sélectionne les lignes qui contiennent un des mots d'une liste de mots-clés.

Synthèse données

Option Compare Text
Sub essai()
  Set d = CreateObject("scripting.dictionary")
  Set f1 = Sheets("base de données")
  Set f2 = Sheets("MotsCherchés")
  BD = f1.Range("A2:C" & f1.[A65000].End(xlUp).Row).Value2
  NbCol = UBound(BD, 2)
  Dim Result()
  n = 0
  mots = f2.Range("A1:A" & f2.[A65000].End(xlUp).Row).Value
  For i = 1 To UBound(BD)
    For k = 1 To NbCol
      For m = 1 To UBound(mots)
        If BD(i, k) Like "*" & mots(m, 1) & "*" Then
          temp = "": For v = 1 To NbCol: temp = temp & BD(i, v): Next v
          If Not d.exists(temp) Then
             n = n + 1: ReDim Preserve Result(1 To NbCol + 1, 1 To n): d(temp) = n
             For j = 1 To NbCol: Result(j + 1, n) = BD(i, j): Next j
           Else
             n = d(temp)
           End If
           Result(1, n) = Result(1, n) & mots(m, 1) & " "
         End If
       Next m
      Next k
    Next i
    f1.[F2:M10000].ClearContents
    f1.[f2].Resize(n, NbCol + 1) = Application.Transpose(Result)
End Sub

Comparaison Find et dictionnaire index

Pour un tableau de 100.0000 lignes et 100 recherches, le temps avec un tableau indexé par un dictionnaire est 10 fois inférieur au temps avec Find.

Compare Find Dictionnaire

Sub essaiFind()   ' 3 sec
  clé = "Nom100000"
  For i = 1 To 100 ' 100 recherches
   Set result = [A2:A100001].Find(what:=clé)
  Next i
  MsgBox result.Offset(, 1).Value
End Sub

Sub essaiDico()  ' 0,3 sec
  Set d = CreateObject("scripting.dictionary")
  TblBD = [A2:B100001].Value
  For i = 1 To UBound(TblBD) ' création index
    d(TblBD(i, 1)) = i
  Next i
  clé = "Nom100000"
  For i = 1 To 100 ' 100 recherches
    pos = d(clé)
    ville = TblBD(pos, 2)
  Next i
  MsgBox ville
End Sub

 


 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 



 

 

 

 

 

 

 

 

 

 

Exemples

Dictionary
Comparaison2BDRapide