Aide Macro : filtrer données + résultats sur nouvelles feuilles

Djorge84

Habitué
Bonjour,

j'ai regardé sur le forum et je n'ai trouvé aucune discussion qui réponde à mon problème. Je vous explique donc de quoi il s'agit et ce que j'ai pu faire jusqu'à maintenant. Si quelqu'un peut me conseiller ensuite...

J'ai une base de donnée dans laquelle la colonne A renseigne sur les années et les premières lignes de la feuille indiquent différents calculs statistiques à partir des données contenues dans les colonnes à partir de F sur la période globale renseignée dans la colonne A (1984-2010).

Or j'aimerai pouvoir obtenir ces mêmes résultats statisitques pour chaque année.

Je sais que des fonctions d'Excel, type Sommeprod, prennent en compte l'utilisation d'un filtre pour calculer des statistiques sur un ensemble de données. Sauf que là des valeurs aberrantes devaient être exclues sur chaque colonnes en fonction d'un critère donné et que le fichier a été construit à partir de fonctions matricielles d'Excel... L'utilisation d'un filtre classique ne permet donc pas d'obtenir les valeurs statistiques sur les éléments retenus.

J'ai donc pensé tout bêtement à copier cette feuille plusieurs fois et à supprimer toutes les valeurs correspondant à l'année que je ne désire pas dans les calculs. Les calculs statisques ne prennent ainsi en compte que les valeurs correspondant à l'année conservée et j'ai donc ce que je souhaite!

Or comme il y a beaucoup d'années (et beaucoup de fichiers...), c'est beaucoup trop long à faire à la main... D'où mon idée de recourir à une macro pour automatiser cette tâche.

Comme je suis débutant en VBA, j'ai utilisé l'enregistreur de macro d'Excel et j'ai obtenu un code qui fonctionne. Pas de problème là-dessus, même si je pense qu'il pourrait êtr eplus joli, on est d'accord... Le problème c'est qu'il fonctionne bien évidemment uniquement pour l'année que j'ai utilisé lors de l'enregistrement : c'est logique (ne vous inquiétez pas j'attendais pas à ce qu'il me résolve toute ma problématique ;) ). Et comme changer à la main l'année dans la macro ne permettrait pas de gagner du temps par rapport à une opération sans macro, j'aurai voulu faire une boucle pour que dans l'ordre la macro :

- copie la feuille qui m'intéresse en dernière position (feuille 2) ;
- supprime toutes les données contenues dans les colonnes à partir de la colonne F ;
- copie la feuille initiale une nouvelle fois (feuille 3) en y insérant 3 lignes tout en haut pour y coller les intitulés des différentes colonnes de données dans le but de réaliser un filtre élaboré;
- copie en A3 l'année que l'on souhaite garder pour le filtre élaboré;
- copier sur la feuille 2, les données provenant de l'utilisation du filtre sur la feuille 3
- renommer les feuille 2 du nom de l'année correspondante aux données;
- supprimer la feuille 3 ;

- et recommencer pour une autre année de la liste...

Pour qu'au final j'ai dans mon fichier, la feuille initiale avec l'ensemble des données et autant d'autres feuilles que d'année indiquée dans la base de données.

J'avais pensé insérer une feuille supplémentaire en 1ère position du classeur dans laquelle j'aurai indiqué sur la première colonne toutes années pour ensuite renvoyer vers ces cellules pour indiquer au filtre élaboré l'année à utiliser (feuille appelée "Année"). Mais je n'arrive pas à faire la boucle...

J'espère avoir été clair.

Si quelqu'un veut bien me filer un coup de main...

Merci par avance

J'indique le code que me donne l'enregistreur de macro :


[cpp]
Sub Test()
' Préparation de la feuille finale

' Copie de la feuille principale en dernière position
Sheets("Régénération").Select
Sheets("Régénération").Copy After:=Sheets(2)
' Sélection de l'ensemble des données sur cette feuille
Rows("27:27").Select
Range(Selection, Selection.End(xlDown)).Select
' Suppression de ces données pour avoir une feuille vide avec la mise en forme et la zone des données statistiques voulues conservée
Selection.ClearContents

' Création d'une feuille intermédiaire pour permettre la manipulation des données

' Copie de la feuille principale en dernière position
Sheets("Régénération").Select
Sheets("Régénération").Copy After:=Sheets(3)
' Insertion de 3 lignes
Rows("1:3").Select
Selection.Insert Shift:=xlDown
' Copie des titres des différents champs pour préparer l'utilisation d'un filtre élaboré
Rows("28:29").Select
Range("F28").Activate
Selection.Copy
' Collage des titres des différents champs sur les deux premières lignes
Rows("1:1").Select
ActiveSheet.Paste
Range("A3").Select
Application.CutCopyMode = False
' Choix de l'année à utiliser pour le filtre élaboré
ActiveCell.Value = Worksheets("Année").Range("A25").Value
Range("A4").Select
' Sélection de la plage de données et de la plage de critères pour le filtre élaboré
Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
Range("A1:AA3"), Unique:=False
' Sélection des données filtrées pour copie
Rows("1212:1212").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy

' Collage des données sur la feuille finale
Sheets("Régénération (2)").Select
Rows("27:27").Select
ActiveSheet.Paste

' Changement du nom de la feuille (année correspondante)
Sheets("Régénération (2)").Name = Sheets("Année").[A25]

' Suppression de la feuille intermédiaire
Sheets("Régénération (3)").Select
Application.CutCopyMode = False
ActiveWindow.SelectedSheets.Delete

End Sub
[/cpp]

PS. J'aurai bien mis un fichier en exemple mais je ne sais pas comment ajouter une PJ à ce post...
 

oozenot

Expert
salut Djorge84,

Bon, avant de commencer a trouver une solution a ton probleme je te propose de d'appliquer quelques "règles" a ton code pour le rendre plus lisible, court et donc compréhensible.

Tout d'abord, tout les . select et les Selection. peuvent être rassemblé. exemple avec tes lignes 9 et 11:
[cpp]
Range(Selection, Selection.End(xlDown)).Select
'// Suppression de ces données pour avoir une feuille vide avec la mise en forme et la zone des données statistiques voulues conservée
Selection.ClearContents
[/cpp]
deviens:
[cpp]
Range(Selection, Selection.End(xlDown)).ClearContents
[/cpp]
et tu peux appliquer ca a tout ton code... ce n'en sera que plus lisible !!

ensuite, pour éviter d'avoir des Active-machin (activesheet) déclare tes feuilles!!!
utilise ce modèle pour la feuille Régénération (2) par exemple:
[cpp]dim ws_regeneration2 as Worksheet
set ws_regeneration2 = Worksheets("Régénération (2)")[/cpp]
et dans la suite de ton code tu n'utilisera que ws_regeneration2 pour désigner ta feuille !

Les lignes 5 et 16 ne servant a rien.
Pour la copie (.copy) il est bien utilisé ligne 17 et moins bien par la suite
utilise la syntaxe suivante : ce_que_tu_veux_copier.Copy la_destination_de_ta_copie

n'hésite pas a user de l'aide Excel F1 sur les fonction comme Copy, l'aide te donne la syntaxe et te dis ce a quoi elle sert !!!!

C'est tout pour le code (pour l'instant).

Maintenant je pensais en lisant le début de ton roman que tu pourrais demander a l'utilisateur d'entrer la date puis la macro exécuterai pour cette date. Mais je me dis que si ton tableau (et oui, Excel est un tableur, pas une base de données!!!!!!) est grands et que ta macro doit s'utiliser sur qqch comme 1000 années différentes... ca va pas le faire !!
Autre solution : demander a l'utilisateur la colonne contenant toute les années et créer une macro qui fais ton analyse pour chaque année contenue dans la colonne (en excluant les doublons, bien sûr !)

au final tu veux copier la ligne qui contient une certaine année dans une autre feuille non?
 

Djorge84

Habitué


Merci pour ta réponse et pour ces éléments.
Je vais prendre en compte tes remarques pour alléger le code dès que j'aurai un moment et je posterai ce que j'aurai obtenu pour qu'on y voit plus clair.

Pour préciser des choses suite à tes diverses propositions, j'ai toutes les années comprises entre 1980 et 2010 dans ma colonne A, donc ça fait 31 années si je ne me trompe pas.

Au départ la solution que tu évoques de faire une macro qui demande à l'utilisateur quelle année il veut traiter était ce que j'envisageais. Mais compte tenu du fait que je ne voyais pas du tout comment on peut faire ça, je me suis plutôt orienté vers l'option "ma macro fait toutes les années qui sont renseignées dans la colonne", d'où mon envie de faire une boucle sur les années.

Donc si tu as des propositions de code à me proposer une fois que j'aurai alléger le code donné par l'enregistreur de macro, je suis preneur des deux solutions! ça pourra toujours m'être utile pour ma culture générale en VBA!

Merci encore, je reviens vers toi le plus vite possible
 

Djorge84

Habitué
Re-bonjour,

donc j'ai essayé les différentes choses que oozenot m'a proposé (faut encore que je me penche sur la fonction copy), mais il y a déjà un problème suite à la déclaration de mes feuilles.

En effet mon classeur Excel a au départ 2 feuilles : Régénération et Années

Donc je déclare ces deux feuilles sans problème (à ce propos pour info pourquoi Worksheet prend un s une fois et pas l'autre dans la déclaration et lorsqu'on attribue un nom à la feuille?)

Les deux autres feuilles Régénération (2) et Régénération (3) n'existent en revanche pas au départ et sont créées par la macro. Et j'obtiens un message d'erreur lorsque je lance la macro (je pense dû à ça) :

ws_regeneration2 = Nothing et Worksheets("Régénération (2)") = <L'indice n'appartient pas à la sélection.>

je pense que ça ferait la même choseà la feuille "Régénération (3)" si ça passait le cap de la feuille "Régénération (2)"

Savez-vous comment résoudre ça?
 

zeb

Modérateur
Salut,

à ce propos pour info pourquoi Worksheet prend un s une fois et pas l'autre dans la déclaration et lorsqu'on attribue un nom à la feuille?
Je réponds à cette question existentielle :

Code:
Dim ws_regeneration2 as Worksheet
ws_regeneration2 sera une et une seule feuille.

Code:
set ws_regeneration2 = Worksheets("Régénération (2)" )
Parmi la collection de toutes les feuilles, on choisit celle qui porte le nom Régénération (2)

Si les bons conseils de oozenot te semblent avisés, refais ton code en les prenant en compte et publie-le. On pourra reprendre à partir de là et mieux comprendre tes explications.
 

Djorge84

Habitué
Bonjour,

après diverses simplifications/modifications, voilà le code que j'obtiens.

[cpp]
Sub NouveauTest()

Dim ws_regeneration As Worksheet
Dim ws_regeneration2 As Worksheet
Dim ws_regeneration3 As Worksheet
Dim ws_annees As Worksheet
Set ws_regeneration = Worksheets("Régénération")
Set ws_regeneration2 = Worksheets("Régénération (2)")
Set ws_regeneration3 = Worksheets("Régénération (3)")
Set ws_annees = Worksheets("Année")

ws_regeneration.Copy After:=Sheets(2)
Rows("27:27").Select
Range(Selection, Selection.End(xlDown)).ClearContents
ws_regeneration.Copy After:=Sheets(3)
Rows("1:3").Insert Shift:=xlDown
Rows("28:29").Copy Rows("1:1")
Range("A3").Select
ActiveCell.Value = ws_annees.Range("A25").Value
Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
Range("A1:AA3"), Unique:=False
Rows("1212:1212").Select
Range(Selection, Selection.End(xlDown)).Copy ws_regeneration2.Rows("27:27")
ws_regeneration2.Name = ws_annees.[A25]
ws_regeneration3.Delete

End Sub
[/cpp]

Effectivement il y avait des choses à supprimer par rapport à la première version...

Le code marche bien tant que je laisse les noms de feuilles donnés par l'enregistreur de macro.
En revanche dès que je déclare les différentes feuilles utilisées (version du code ci-dessus), ça ne marche plus... et il bloque sur la première feuille qui au départ n’existe pas et qui doit être créée par la macro on dirait (ligne 8 du code).

Je ne sais pas comment je peux vous mettre en ligne une version du fichier Excel pour que vous voyez ce que ça doit faire exactement. Ça serait plus simple sans doute.
 

zeb

Modérateur
Meuh non !
Vous devez être connecté pour voir les images.


Ta feuille Régénération (2) n'existe pas encore en ligne 8. Elle est créé par copie en ligne 12.

Donc retire la ligne 8, et écris la ligne 12 comme ceci :
Code:
Set ws_regeneration2 = ws_regeneration.Copy(After:=Sheets(2))

Lignes 13 et 14. Non môssieu :non: Il reste un couple Select/Selection. Spa bon. Et en plus, on ne sait pas de quelle feuille tu parles ! Et pis tu ne respectes pas la fonction Rows(), la pauvre. Tu lui fais faire trop de boulot ( ;) ).
Code:
' // Pas bon du tout
Rows("27:27" ).Select
Range(Selection, Selection.End(xlDown)).ClearContents

' // Un peu mieux
ws_xxxx.Rows(27).Select
ws_xxxx.Range(Selection, Selection.End(xlDown)).ClearContents

' // Correct
ws_xxxx.Range(ws_xxxx.Rows(27), ws_xxxx.Rows(27).End(xlDown)).ClearContents

' // Plus léger (ou pas)
With ws_xxxx
  .Range(.Rows(27), .Rows(27).End(xlDown)).ClearContents
End With

_________

Je ne sais pas comment je peux vous mettre en ligne une version du fichier Excel pour que vous voyez ce que ça doit faire exactement. Ça serait plus simple sans doute.
Non plus môssieu. On ne met pas en ligne des fichiers pleins de macros. C'est pour le bien de tous !
 

Djorge84

Habitué
Re-

je ne savais pas comment faire pour supprimer les Select/Selection dans les Range, désolé :(
Bon en tout cas, voilà qui est fait et je pense que vous y verez plus clair maintenant.
Pour la déclaration des feuilles, je n'ai pas fait aussi concis que ce que tu m'as dit dans ton dernier post car ça ne fonctionnait pas... J'ai donc opté pour la solution en 2 étapes: je copie la feuille et je la déclare en tant que ws_xxxx ensuite.

[cpp]
Sub NouveauTest()

Dim ws_regeneration As Worksheet
Dim ws_regeneration2 As Worksheet
Dim ws_regeneration3 As Worksheet
Dim ws_annees As Worksheet

Set ws_regeneration = Worksheets("Régénération")
Set ws_annees = Worksheets("Année")
ws_regeneration.Copy After:=Sheets(2)
Set ws_regeneration2 = Worksheets("Régénération (2)")
ws_regeneration.Copy After:=Sheets(3)
Set ws_regeneration3 = Worksheets("Régénération (3)")
ws_regeneration2.Range(ws_regeneration2.Rows(27), ws_regeneration2.Rows(27).End(xlDown)).ClearContents
ws_regeneration3.Range(ws_regeneration3.Rows(1), ws_regeneration3.Rows(3)).Insert Shift:=xlDown
ws_regeneration3.Range(ws_regeneration3.Rows(28), ws_regeneration3.Rows(29)).Copy ws_regeneration3.Rows(1)
ws_regeneration3.Range("A3") = ws_annees.Range("A25").Value
ws_regeneration3.Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
Range("A1:AA3"), Unique:=False
ws_regeneration3.Range(ws_regeneration3.Rows(1212), ws_regeneration3.Rows(1212).End(xlDown)).Copy ws_regeneration2.Rows("27:27")
ws_regeneration2.Name = ws_annees.[A25]
ws_regeneration3.Delete

End Sub
[/cpp]

Si vous pouvez me donner des pistes pour qu'à partir de ma feuille ws_regeneration (où j'ai toutes mes données triées par année --> colonne A), je puisse créer des feuilles relatives chacune à une année donnée en allant chercher celle que je veux dans ma feuille ws_annes, ça me sauve!

Merci de tous vos conseils en tout cas, j'apprends petit à petit ...
 

zeb

Modérateur
[strike]Alors ça, ce n'est pas une bonne idée :[/strike]
Code:
ws_regeneration.Copy After:=Sheets(2)
Set ws_regeneration2 = Worksheets("Régénération (2)" )
[strike]Pourquoi ?
Et bien parce que tu présumes du nom de ta nouvelle feuille. Or, si une feuille de ce nom là existe déjà, le nom de la nouvelle n'est pas celui que tu attends. Et si ta macro devait être utilisée sur un Excel dans une autre langue ? Ou bien, ce nommage perdurera-t-il dans la prochaine version d'Excel ou avec le prochain système d'exploitation ? (Que de souvenirs pour moi dans les diverses entreprises où j'ai bossé :lol: )

Donc : [/strike]
Code:
Set ws_regeneration2 = ws_regeneration.Copy(After:=Sheets(2))

Bon maintenant que tu as copié trois fois tes données, il faut supprimer dans chacune des nouvelles feuilles les données en trop. Pour ça, parcours tes données en partant du bas et supprime les lignes qui ne te conviennent pas.

J'explique pourquoi il faut partir du bas.

Je te laisse trouver et nous proposer un truc pour le parcours de tes données. Si ça marche, tant mieux :sol: sinon, on t'aidera à en faire quelque chose de fonctionnel ;)
 

Djorge84

Habitué
Ok je comprends la différence entre les 2 façons de faire pour la copie et déclaration des nouvelles feuilles créées et le problème que peut entrainer la solution pour laquelle j'ai opté. Mais lorsque je fais comme tu me dis, cela conduit à une erreur de compilation et j'ai le message suivant:

Erreur de compilation:
Fonction ou variable attendue

Et dans la ligne de code

[cpp] Set ws_regeneration2 = ws_regeneration.Copy(After:=Sheets(2)) [/cpp]

j'ai .Copy qui apparait surligné en jaune.

C'est d'ailleurs pour cette raison que j'avais pris l'option de faire ça en 2 lignes (même je n'avais pas vu l'inconvénient que tu mentionne)

Je vais regarder ce que tu mentionne pour la sélection des données à partir du bas.
 

zeb

Modérateur
:pfff:

Tu as parfaitement raison, le code que je te proposais ne fonctionne pas. Le modèle objet d'Excel est incomplet. Quelle bande de nases chez Crosoft :fou: Font les choses à moitié...

Bon, comme j'ai cité pleins de bonnes raison pour pas se fier au nom, il faut maintenant que je te montre comment faire autrement. C'est facile. Un classeur, ça contient des onglets de plusieurs natures, feuilles de calcul et graphiques essentiellement, que l'on retrouve respectivement dans les collections Worksheets et Charts. La collection de tous les onglets, quelque soit leur nature, est Sheets. Donc, si tu demandes à ce que ta nouvelle feuille soit copiée après (Copy After:=) la feuille 2, c'est que la nouvelle feuille va être numérotée 3, et que toutes les autres vont être décalées dans la collection. T'as tout compris ? :pt1cable:

Un exemple sera plus parlant :

Code:
Dim n As Integer
Dim ws_feuillecible_A As Worksheet
Dim ws_feuillecible_B As Worksheet

n = 2
ws_feuillesource.Copy After:=Sheets(n)
Set ws_feuillecible_A = Sheets(n + 1)

ws_feuillesource.Copy Before:=Sheets(n)
Set ws_feuillecible_B = Sheets(n)

Evidemment, dans ton code, tu te permettras de ne pas te servir d'une variable ;)
Sauf que tu peux fixer la valeur de n à Sheets.Count, pour te référer à la dernière feuille, ce qui est particulièrement élégant :o

----------------

Bon, et ta proposition d'un truc pour le parcours de tes données ?
 

Djorge84

Habitué
Bon alors je pense avoir pris en compte tes différentes remarques et ça fonctionne, c'est bon.

A l'arrivée mon classeur contient bien mes feuilles ws_annees et ws_regeneration initiales, ainsi que la feuille qui m'intéresse, obtenue grâce à la macro et nommée ws_annees.[A25] (2004 en fait).

Le truc maintenant c'est qu'il faut que j'arrive à faire une boucle pour obtenir la même chose mais sur l'ensemble des années renseignées sur ma feuille ws_annees , sur laquelle des années sont indiquées sur la plage A2:A31. Donc il faut que la boucle porte sur l'intégralité du code pour avoir au final un onglet par année. Et là je bloque car je ne vois comment m'en sortir, notamment avec l'indexation des différentes feuilles et de celle que je crée de façon temporaire pour faire mon filtre.

Un conseil?

[cpp]
Sub NouveauTest()

Dim n As Integer
Dim ws_regeneration As Worksheet
Dim ws_regeneration2 As Worksheet
Dim ws_regeneration3 As Worksheet
Dim ws_annees As Worksheet
Dim derli As Long
Dim i As Long

n = Sheets.Count
Set ws_regeneration = Worksheets("Régénération")
Set ws_annees = Worksheets("Année")
ws_regeneration.Copy After:=Sheets(n)
Set ws_regeneration2 = Sheets(n + 1)
ws_regeneration.Copy After:=Sheets(n + 1)
Set ws_regeneration3 = Sheets(n + 2)

ws_regeneration2.Range(ws_regeneration2.Rows(27), ws_regeneration2.Rows(27).End(xlDown)).ClearContents
ws_regeneration3.Range(ws_regeneration3.Rows(1), ws_regeneration3.Rows(3)).Insert Shift:=xlDown
ws_regeneration3.Range(ws_regeneration3.Rows(28), ws_regeneration3.Rows(29)).Copy ws_regeneration3.Rows(1)
ws_regeneration3.Range("A3") = ws_annees.Range("A25").Value
ws_regeneration3.Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
Range("A1:AA3"), Unique:=False

derli = ws_regeneration3.Columns(1).Find("*", , , , , xlPrevious).Row
For i = derli To 30 Step -1
If ws_regeneration3.Cells(i, 1) = "" Then
ws_regeneration3.Rows(i).Delete
End If
Next

ws_regeneration3.Range(ws_regeneration3.Rows(30), ws_regeneration3.Rows(30).End(xlDown)).Copy ws_regeneration2.Rows("27:27")
ws_regeneration2.Name = ws_annees.[A25]
ws_regeneration3.Delete

End Sub
[/cpp]


PS. Je ne sais pas si la façon dont j'utilise Sheets.Count et l'incrémentation des Sheets(xx) est bonne (optimale ?) ou pas en revanche...
 

zeb

Modérateur
PS:

Oui, c'est bon.
Mais tu peux aussi faire comme ça :
Code:
ws_regeneration.Copy After:=Sheets(Sheets.Count)
Set ws_regeneration2 = Sheets(Sheets.Count)
ws_regeneration.Copy After:=Sheets(Sheets.Count)
Set ws_regeneration3 = Sheets(Sheets.Count)
Attention, c'est Sioux ! ;)

Juste comme ça, t'as pas envie d'appeler tes variables avec des noms plus courts et/ou des abréviations ?

-------------

Petits bouts de code à étudier :
Code:
Function SheetExists(ByVal name As String, Optional wb As Workbook) As Boolean
    Dim sheet As Object
    
    If IsMissing(wb) Or wb Is Nothing Then Set wb = ActiveWorkbook
    
    For Each sheet In wb.Sheets
        If sheet.name = name Then
            SheetExists = True
            Exit For
        End If
    Next
End Function
Code:
Dim zone      As Range
Dim cell      As Range
Dim annees()  As Boolean
Dim annee_min As Integer
Dim annee_max As Integer
Dim annee     As Integer

Dim ws_source As Worksheet

Set ws_source = Worksheets("xxxxxxxxx")

Set zone = ws_source.Range("A2:A31")

MsgBox "La zone prise en compte est : " & zone.Address

annee_min = WorksheetFunction.Min(zone)
annee_max = WorksheetFunction.Max(zone)

MsgBox "L'année la plus ancienne est " & annee_min & ", et la plus récente est " & annee_max

ReDim annees(annee_min To annee_max)
   
For Each cell In zone
    If Not IsEmpty(cell.Value) And IsNumeric(cell.Value) Then
        If Not annees(CInt(cell.Value)) Then MsgBox "Prise en compte de l'année " & CInt(cell.Value)
        annees(CInt(cell)) = True
    End If
Next

For annee = annee_min To annee_max
    If annees(annee) And Not SheetExists(CStr(annee)) Then
        ws_source.Copy , Sheets(Sheets.Count)
        Sheets(Sheets.Count).name = CStr(annee)
    End If
Next
 

Djorge84

Habitué
je viens de regarder ce que tu as répondu à mon dernier post et je sèche complètement sur le premier bout de code.
Je comprends que la fonction permet de vérifier si une feuille existe déjà sous un nom donné et que tu utilise cette fonction dans la dernière boucle du second bout de code. Mais pour ce qui est de la façon dont tu la code et de la première condition... :??:

Que signifient dans la fonction SheetExists : ByVal name As String, Optional wb As Workbook ?
Et la conditon : If IsMissing(wb) Or wb Is Nothing Then Set wb = ActiveWorkbook ?

Pour le second je vois à peu près de quoi il s'agit, excepté pour la boucle :

[cpp]For Each cell In zone
If Not IsEmpty(cell.Value) And IsNumeric(cell.Value) Then
If Not annees(CInt(cell.Value)) Then MsgBox "Prise en compte de l'année " & CInt(cell.Value)
annees(CInt(cell)) = True
End If
Next[/cpp]

C'est les conditions que tu utilises qui me perdent complètement...

Il faut que je fasse quoi de ces deux bouts de code maintenant: que je les adapte à mon cas ou que j'y insère ce que j'ai réussi à coder jusqu'à maintenant grâce à vous? :(


 

zeb

Modérateur
Ce que je te propose est à étudier. Donc tu dois te poser pleins de questions pour bien le comprendre. Je vois que certaines de ces questions sont pour moi :p

La fonction SheetExists accepte un ou deux paramètres, un nom de feuille, et optionnellement, un classeur. Connais-tu la notion de passage de paramètre par valeur ou par référence ? Si oui, ByVal devrait être maintenant explicite pour toi. Sinon, pour ta culture informatique, renseigne-toi sur le sujet. C'est tellement classique que tu ne manqueras pas de trouver.

Or donc, le classeur est optionnel. Donc s'il est omis ou s'il n'a pas de valeur, je lui en fournis une. Observes-en bien l'intérêt. Si tu ne traites qu'un seul classeur, tu peux ne pas t'en soucier, et ne rien préciser. C'est ce que je fais à la ligne 31. Par contre, si tu veux traiter plusieurs classeurs dans une même macro, plutôt que te fier à celui qui serait actif, tu peux explicitement le préciser.

Ce type de fonction, très générique, qui ne fait pas grand'chose mais qui le fait bien, s'appelle une primitive. J'en ai plein, et je te les ressors au besoin, sans les simplifier, ce qui peut te dérouter. N'en prends pas ombrage au contraire, profites-en ;)

Maintenant la boucle. J'ai fait un tableau avec comme indice, les valeurs trouvées dans la colonne des années. Je ne pars pas de zéro, ni de un, et je ne vais pas jusqu'à l'an 3000, ce que j'aurais pu faire. Je le taille au plus juste.

Ensuite, je boucle. Ah, mais si il n'y a pas que des nombres dans ta colonne ? Et si une cellule est vide ? On ne sait jamais ! Donc je vérifie que la cellule est non vide et que la valeur est numérique. Je ne peux pas me contenter de vérifier que la valeur est numérique, parce que cette andouille de fonction Excel IsNumeric() renvoie vrai quand la cellule est vide :pfff:

Réétudie tout ça à la lumière de ces quelques renseignements supplémentaires. Et surtout n'hésite pas à reposer des questions.

Et tu me dois toujours une proposition de code pour parcourir tes données pour supprimer celles qui ne nous intéressent pas. :o
(Je ne t'en donnerai pas le code, je partirai du tien pour l'améliorer si ce que tu proposes ne fonctionne pas parfaitement ;) )
 

Djorge84

Habitué
OK merci des précisions qui m'aident grandement! :D

Je vais regarder les bouts de code que tu m'as proposé à la lumière de tes explications.

Pour ce qui concerne la proposition de code que je dois te faire, je pensais que celle que j'ai ajouté dans le code que j'ai posté correspondait à ce que tu me demandais... Je remets la partie en question:

[cpp]derli = ws_regeneration3.Columns(1).Find("*", , , , , xlPrevious).Row
For i = derli To 30 Step -1
If ws_regeneration3.Cells(i, 1) = "" Then
ws_regeneration3.Rows(i).Delete
End If
Next[/cpp]

J'ai également essayé de faire quelquechose du genre sur ma feuille source ws_regene (ici dans le cas de l'année 2004) :

[cpp]derli = ws_regene.Columns(1).Find("*", , , , , xlPrevious).Row
For i = derli To 27 Step -1
If ws_regene.Cells(i, 1) <> 2004 Then
ws_regene.Rows(i).Delete
End If
Next[/cpp]

sauf que vu que j'ai quelquechose comme 10 000 lignes, cette opération doit être trop gourmande en mémoire et au bout de 15 min Excel moulinait toujours... Je l'ai donc killé. Il y a peut être une astuce pour faciliter cette action, mais mes faibles compétences en VB ne me permettent pas de voir quoi...

PS. Je ne connais pas la notion de passage de paramètre par valeur ou par référence --> je vais me renseigner ;)
 

zeb

Modérateur
Arf, au temps pour moi. Je l'avais raté.
Oui, c'est plutôt pas mal comme solution.

Renseigne-toi aussi sur ScreenUpdating. Il se pourrait que tu apprécies ;)
 

zeb

Modérateur
Si tes traitements sont un peu longs, tu peux mettre la progression dans la barre de statut d'Excel, c'est sympa :

Code:
For i = derli To 27 Step -1
    Application.StatusBar = "Travail en cours. Progression : " & (derli - i) * 100 \ (derli - 27) & "%"
    ...
Next
Application.StatusBar = False
 

Djorge84

Habitué


Effectivement ça change les choses de désactiver le ScreenUpdating... :)
 
Vous devez vous inscrire ou vous connecter pour répondre ici.
Derniers messages publiés
Statistiques globales
Discussions
730 129
Messages
6 717 853
Membres
1 586 373
Dernier membre
https://forum.tomshardwar
Partager cette page
Haut