Liste consolidée filtrée multicritère utilisant Array et/ou dictionary
Bonjour à tous,
Je commence par le vif du sujet pour faciliter la lecture de gens qui comme moi passent des heures à lire des sujets pour se dépanner :
Objectif : virtualiser une base de donnée dans un array ou un dictionnaire pour relever l'ensemble des clés qui correspondent à un critère donné.
Une feuille base de donnée dans "PRIO" : clés en colonne A ; codes en colonne C.
La feuille "filtre" liste en colonne "D" l'ensemble des codes servant de critère.
En feuille "APPRO" je souhaite récupérer la liste exhaustive des clés qui correspondent aux critères "codes".
Bonus : a cela s'ajoute l'existence d'une seconde base de données "AX" (clés colonne A ; codes colonne E) qui est l'image actualisée de "PRIO" et dont il faudra venir puiser là aussi la liste exhaustive des clés correspondants aux critères "codes" sans créer de doublon cependant.
J'ai commencé mes premiers programmes il y une semaine et malgré de longues lectures et recherches je peine à utiliser correctement les tableaux et dictionnaires.
Le travail s'effectue sur une quantités de données (2BD de 15000 lignes) et obligatoirement en VBA. Si pas d'utilisation de tableau ou de dico ca met un temps monstre donc on écarte.
Solution qui me semble adaptée :
1/ passer la BD "PRIO" en array ;
1optionnel/ passer la liste des critères en array ;
2/double boucle : sur chaque ligne de l'array dont le code appartient à la liste de critères-code : ajouter le couple clé + code à un dictionnaire (ou un nouvel array) ;
3/réitérer avec la BD "AX" puis dégommer les doublons
4/ coller cette liste à 2 colonnes dans la feuille "APPRO"
Je but depuis 2 jours sur ce problème que je n'arrive pas à contourner de par mon inexpérience car il s'agit à mon avis, d'un problème de qualification de variables ou autre aspect élémentaire mais voila... je bloque !
Merci d'avance à vous qui voudrez vous pencher sur mon problème et m'apporter votre aide : fichier en PJ + bout de code suivant : jusqu'à REPERE 1 rien d'intéressant ;)
Option Explicit
Sub Export_APPRO2()
'On Error Resume Next
Dim MonDico As Object
Dim I As Integer: Dim k As Integer: Dim n As Integer
Dim LastRow As Integer
Dim TblPRIO() As Variant
Dim TblAX() As Variant
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
LastRow = Sheets("filtres").Cells(Rows.count, "D").End(xlUp).Row
n = LastRow
If n < 4 Then
MsgBox "Il n'y a aucun fournisseur dans la liste !"
Else
'Clear des anciens clé_CONCATENER du fichier APPRO
LastRow = Sheets("APPRO").Cells(Rows.count, "A").End(xlUp).Row
If Not LastRow < 2 Then
Sheets("APPRO").Range("$A$2:$B" & LastRow).ClearContents
End If
Set MonDico = Nothing
Set MonDico = CreateObject("Scripting.Dictionary")
Barre_progression.afficher
LastRow = Sheets("PRIO").[A15000].End(xlUp).Row
TblPRIO = Sheets("PRIO").Range("$A$2:$C" & LastRow).Value
'******************************REPERE 1******************************
Debug.Print TblPRIO(1, 3) 'LECTURE IMPOSSIBLE
'******************************REPERE 1******************************
'Les clé_CONCATENER/Item_PARMA#C du PRIO sont ajoutées à MonDico si parma dans la liste des frn
For k = 4 To n
Barre_progression.actualiser "Export commandes PRIO...", CInt(99 * ((k - 3) / (n - 3)))
For I = 0 To UBound(TblPRIO, 1)
'******************************REPERE 2******************************
If Sheets("filtres").Cells(k, "D").Value = TblPRIO(I, 3) Then 'pas certain de pouvoir comparer ces valeurs comme ceci
'******************************REPERE 2******************************
MonDico.Add TblPRIO(I, 1), _
TblPRIO(I, 3)
End If
Next I
Next k
'******************************SUITE DU CODE : problème sera équivalent******************************
LastRow = Sheets("AX").[A15000].End(xlUp).Row
TblAX = Sheets("AX").Range("$A$2:$E" & LastRow).Value
'Les clé_CONCATENER/Item_PARMA#E du AX sont ajoutées à MonDico si n'y figuraient pas déjà et si parma dans la liste des frn
For k = 4 To n
Barre_progression.actualiser "Export commandes AX...", CInt(99 * ((k - 3) / (n - 3)))
For I = 0 To UBound(TblAX)
If Sheets("filtres").Cells(k, "D").Value = TblAX(I, 5) Then
If Not MonDico.Exists(TblAX(I, 1)) Then
MonDico.Add TblAX(I, 1), _
TblAX(I, 5)
End If
End If
Next I
Next k
n = MonDico.count
Sheets("APPRO").[$A$2].Resize(MonDico.count) = Application.Transpose(MonDico.keys)
Sheets("APPRO").[$B$2].Resize(MonDico.count) = Application.Transpose(MonDico.items)
Set MonDico = Nothing
MsgBox n & " ligne(s) de commande(s) exportée(s) avec succès !"
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Barre_progression.actualiser "Export...", CInt(100)
End Sub
bonjour tomato,
j'ai modifié un petit peu votre macro. Ces "progresbar" ne servent qu'à ralentir la macro
Bonjour et mille mercis Bart pour votre réponse + que bienvenue !
Une bonne et une mauvaise nouvelle: ca fonctionne .. mais qu'à moitié.
Le code est opérationnel mais les nouvelles clés dans la BD "AX" ne sont pas intégrées.. et je ne l'explique pas !
Quelques commentaires ci après :
Option Explicit
Sub Export_APPRO2()
'On Error Resume Next
Dim MonDico As Object
Dim I As Integer: Dim k As Integer: Dim n As Integer
Dim LastRow As Integer
Dim TblPRIO() As Variant
Dim TblAX() As Variant
Dim X, r, t 'les 2 sont variant !!!
t = Timer
LastRow = Sheets("filtres").Cells(Rows.Count, "D").End(xlUp).Row
n = LastRow
If n < 4 Then
MsgBox "Il n'y a aucun fournisseur dans la liste !"
Else
X = Sheets("filtres").Cells(4, "D").Resize(Application.Max(2, n)).Value 'array temporaire de D
'*****1/5: pourquoi déclarer "Dim X As Variant" et non pas "Dim X() As Variant ?"*****
'Clear des anciens clé_CONCATENER du fichier APPRO
LastRow = Sheets("APPRO").Cells(Rows.Count, "A").End(xlUp).Row
If Not LastRow < 2 Then Sheets("APPRO").Range("$A$2:$B" & LastRow).ClearContents
Set MonDico = CreateObject("Scripting.Dictionary")
LastRow = Sheets("PRIO").[A15000].End(xlUp).Row
TblPRIO = Sheets("PRIO").Range("$A$2:$C" & LastRow).Value
'Les clé_CONCATENER/Item_PARMA#C du PRIO sont ajoutées à MonDico si parma dans la liste des frn
For I = 1 To UBound(TblPRIO, 1)
'*****2.1/5 : pourquoi ne démarre t on pas à I = -1 ? Nous n'avons pourtant pas déclaré de base 1 sur l'array... ;*****
'*****2.2/5 : Est ce que "UBound(TblPRIO, 1)" peut se simplifier à "UBound(TblPRIO)" ?*****
If IsNumeric(r) Then
r = Application.Match(TblPRIO(I, 3), X, 0) 'clé existe ?
'*****3/5 : j'aurais eu le réflex de boucler dans la boucle sur chaque valeur dans X dans le genre de "For J = -1 To UBound(X)" et "r = Application.Match(TblPRIO(I, 3), X(J), 0)". Le fait d'utiliser X "seul" permet de s'en affranchir c'est bien ca ?"*****
If IsNumeric(r) Then
If Not MonDico.exists(TblPRIO(I, 1)) Then MonDico.Add TblPRIO(I, 1), TblPRIO(I, 3) 'non = ajouter au dictionaire
End If
Next I
LastRow = Sheets("AX").[A15000].End(xlUp).Row
TblAX = Sheets("AX").Range("$A$2:$E" & LastRow).Value
'Les clé_CONCATENER/Item_PARMA#E du AX sont ajoutées à MonDico si n'y figuraient pas déjà et si parma dans la liste des frn
For I = 1 To UBound(TblAX)
r = Application.Match(TblAX(I, 5), X, 0) 'clé existe ?
If IsNumeric(r) Then
If Not MonDico.exists(TblAX(I, 1)) Then MonDico.Add TblAX(I, 1), TblAX(I, 5) 'oui = ajouter au dictionaire
'*****4/5 : j'ai juste modifié de "TblAX(I, 3)" à TblAX(I, 5) car on cherche la colonne critère en "E" et plus "D""*****
End If
Next
If MonDico.Count > 0 Then
Sheets("APPRO").[$A$2].Resize(MonDico.Count) = Application.Transpose(MonDico.keys)
Sheets("APPRO").[$B$2].Resize(MonDico.Count) = Application.Transpose(MonDico.items)
MsgBox MonDico.Count & " ligne(s) de commande(s) exportée(s) avec succès !" & vbLf & "temps nécessaire : " & Format(Timer - t, "0.00\s")
End If
Set MonDico = Nothing
End If
End Sub
'*****5/5 : malheureusement après essai je constate que les nouvelles clés qui peuvent être uniquement présentes dans la BD "AX" ne sont pas relevées... je ne l'explique pas"*****re,
Déclarer les variables, bon, il y a (en binaire) 10 sorte de personnes, ceux qui le font et le reste et moi je me situe dans le 2eme groupe (par conviction, pas par paresse et la déclaration, ce n'est que'une détection vite d'erreur).
Ce X est une variable auxiliaire polyvalente temporaire que j'utilise partout pour quelque chose de courte durée, un moment, il est variant, un moment plus tard double ou ... juste pour quelque lignes dans la macro. Ici, c'est temporairement le contenu de la colonne D de "Filtres"
Si on lit le contenu d'une plage, le "LBound" (sorte de limite inférieure) est 1 et ne pas 0.
Oui, UBound(TblPRIO, 1)=UBound(TblPRIO)
Vous parlez de 15.000 lignes et par exemple 20 clés dans filtre, alors il faut boucler ces 15.000 lignes 20 fois, maintenant seulement 1 fois, donc 20 fois plus vite. Si vous voulez un triage sur ce clé, on peut le faire après dans la feuille "Appro".
J'ai déclaré "r" comme variant, le résultat du "application.match" sera un chiffre (clé présent) ou une erreur (clé n'existe pas), mais comme r est variant, la macro ne s'arrête pas et la ligne suivante, je n'ai qu'à vérifier si r est numérique donc le clé est présent dans la colonne D de "filtres".
oui, ce TblAX(I, 5) était faux.
'*****5/5 : malheureusement après essai je constate que les nouvelles clés qui peuvent être uniquement présentes dans la BD "AX" ne sont pas relevées... je ne l'explique pas"*****
Vous voulez des clés qui n'existent pas encore dans la colonne D de "filtres" ? Alors, c'est inverse et il faut ajouter "not" a cette ligne
If not IsNumeric(r) ThenMerci beaucoup pour tes réponses et ton aide.
Je place le sujet résolu.
En effet, la non détection des clés de la seconde BD venait du fait que dans cette BD là, les données de la colonne A (clés) n'étaient pas des valeurs en dur mais des formules qui pouvait renvoyer une chaine vide (= "").
J'ai résolu la situation en détectant la dernière ligne comme suit :
LastRow = Sheets("AX").Range("A:A").Find("*", , xlValues, , , xlPrevious).RowMerci à toi de faire vivre cet espace d'entre aide et d'apprentissage. Bonne continuation :)
re, attention, ce "lastrow" ignore les cellules invisibles (=filtré) de la colonne A, donc supposons que A1000<>"" mais sa ligne est filtrée/cachée et le résultat suivant est A100 qui est visible, alors LastRow=100 !!!!
Alors, commencer avec un reset des filtres oubien une fonction personnalisée.
Sub Teste()
MsgBox DerniereLigne_ColA("AX", 1000) 'se limiter aux premiere 1.000 lignes
MsgBox DerniereLigne_ColA("AX") 'la colonne A complète
End Sub
Function DerniereLigne_ColA(Feuille As String, Optional MaxLignes As Long)
If MaxLignes = 0 Then MaxLignes = Rows.Count
Sheets(Feuille).Range("A1").Resize(MaxLignes).Name = "My_A"
x = Evaluate("iferror(aggregate(14,6,row(my_A)/(my_a<>""""),1),0)")
DerniereLigne_ColA = IIf(x = 0, "Erreur", x)
End Functionou des autres propositions ....
L'information qui... ne m'arrange pas :(
Je n'ai pas assez de skills pour comprendre ta fonction pour le moment. Je pense donc me diriger vers un lastrow de la colonne B de ma BD "AX" (normalement il y en a autant que de clés) mais ce n'est pas idéal.
C'est dingue qu'il n'y ait pas une bidouille en une ligne simple pour trouver un .end(XlUp) and <> "" :(
Merci tout de même :/
c'est simple, n'utilisez pas l'autofilter avant de chercher la dernière ligne.