Se connecter avec
S'enregistrer | Connectez-vous

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

Dernière réponse : dans Programmation

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 :


  1. Sub Test()
  2. ' Préparation de la feuille finale
  3.  
  4. ' Copie de la feuille principale en dernière position
  5. Sheets("Régénération").Select
  6. Sheets("Régénération").Copy After:=Sheets(2)
  7. ' Sélection de l'ensemble des données sur cette feuille
  8. Rows("27:27").Select
  9. Range(Selection, Selection.End(xlDown)).Select
  10. ' 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
  11. Selection.ClearContents
  12.  
  13. ' Création d'une feuille intermédiaire pour permettre la manipulation des données
  14.  
  15. ' Copie de la feuille principale en dernière position
  16. Sheets("Régénération").Select
  17. Sheets("Régénération").Copy After:=Sheets(3)
  18. ' Insertion de 3 lignes
  19. Rows("1:3").Select
  20. Selection.Insert Shift:=xlDown
  21. ' Copie des titres des différents champs pour préparer l'utilisation d'un filtre élaboré
  22. Rows("28:29").Select
  23. Range("F28").Activate
  24. Selection.Copy
  25. ' Collage des titres des différents champs sur les deux premières lignes
  26. Rows("1:1").Select
  27. ActiveSheet.Paste
  28. Range("A3").Select
  29. Application.CutCopyMode = False
  30. ' Choix de l'année à utiliser pour le filtre élaboré
  31. ActiveCell.Value = Worksheets("Année").Range("A25").Value
  32. Range("A4").Select
  33. ' Sélection de la plage de données et de la plage de critères pour le filtre élaboré
  34. Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
  35. Range("A1:AA3"), Unique:=False
  36. ' Sélection des données filtrées pour copie
  37. Rows("1212:1212").Select
  38. Range(Selection, Selection.End(xlDown)).Select
  39. Selection.Copy
  40.  
  41. ' Collage des données sur la feuille finale
  42. Sheets("Régénération (2)").Select
  43. Rows("27:27").Select
  44. ActiveSheet.Paste
  45.  
  46. ' Changement du nom de la feuille (année correspondante)
  47. Sheets("Régénération (2)").Name = Sheets("Année").[A25]
  48.  
  49. ' Suppression de la feuille intermédiaire
  50. Sheets("Régénération (3)").Select
  51. Application.CutCopyMode = False
  52. ActiveWindow.SelectedSheets.Delete
  53.  
  54. End Sub


PS. J'aurai bien mis un fichier en exemple mais je ne sais pas comment ajouter une PJ à ce post...
Lassé par la pub ? Créez un compte
Expert Programmation

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:
  1. Range(Selection, Selection.End(xlDown)).Select
  2. '// 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
  3. Selection.ClearContents

deviens:
  1. Range(Selection, Selection.End(xlDown)).ClearContents

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:
  1. dim ws_regeneration2 as Worksheet
  2. set ws_regeneration2 = Worksheets("Régénération (2)")

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?

oozenot a dit :
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:
  1. Range(Selection, Selection.End(xlDown)).Select
  2. '// 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
  3. Selection.ClearContents

deviens:
  1. Range(Selection, Selection.End(xlDown)).ClearContents

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:
  1. dim ws_regeneration2 as Worksheet
  2. set ws_regeneration2 = Worksheets("Régénération (2)")

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?


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

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?
Expert Programmation

Salut,

Citation :
à 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 :

  1. Dim ws_regeneration2 as Worksheet

ws_regeneration2 sera une et une seule feuille.

  1. 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.

Bonjour,

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

  1. Sub NouveauTest()
  2.  
  3. Dim ws_regeneration As Worksheet
  4. Dim ws_regeneration2 As Worksheet
  5. Dim ws_regeneration3 As Worksheet
  6. Dim ws_annees As Worksheet
  7. Set ws_regeneration = Worksheets("Régénération")
  8. Set ws_regeneration2 = Worksheets("Régénération (2)")
  9. Set ws_regeneration3 = Worksheets("Régénération (3)")
  10. Set ws_annees = Worksheets("Année")
  11.  
  12. ws_regeneration.Copy After:=Sheets(2)
  13. Rows("27:27").Select
  14. Range(Selection, Selection.End(xlDown)).ClearContents
  15. ws_regeneration.Copy After:=Sheets(3)
  16. Rows("1:3").Insert Shift:=xlDown
  17. Rows("28:29").Copy Rows("1:1")
  18. Range("A3").Select
  19. ActiveCell.Value = ws_annees.Range("A25").Value
  20. Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
  21. Range("A1:AA3"), Unique:=False
  22. Rows("1212:1212").Select
  23. Range(Selection, Selection.End(xlDown)).Copy ws_regeneration2.Rows("27:27")
  24. ws_regeneration2.Name = ws_annees.[A25]
  25. ws_regeneration3.Delete
  26.  
  27. End Sub


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.
Expert Programmation

Meuh non !

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 :
  1. 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 ( ;)  ).
  1. ' // Pas bon du tout
  2. Rows("27:27" ).Select
  3. Range(Selection, Selection.End(xlDown)).ClearContents
  4.  
  5. ' // Un peu mieux
  6. ws_xxxx.Rows(27).Select
  7. ws_xxxx.Range(Selection, Selection.End(xlDown)).ClearContents
  8.  
  9. ' // Correct
  10. ws_xxxx.Range(ws_xxxx.Rows(27), ws_xxxx.Rows(27).End(xlDown)).ClearContents
  11.  
  12. ' // Plus léger (ou pas)
  13. With ws_xxxx
  14. .Range(.Rows(27), .Rows(27).End(xlDown)).ClearContents
  15. End With


_________

Citation :
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 !

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.

  1. Sub NouveauTest()
  2.  
  3. Dim ws_regeneration As Worksheet
  4. Dim ws_regeneration2 As Worksheet
  5. Dim ws_regeneration3 As Worksheet
  6. Dim ws_annees As Worksheet
  7.  
  8. Set ws_regeneration = Worksheets("Régénération")
  9. Set ws_annees = Worksheets("Année")
  10. ws_regeneration.Copy After:=Sheets(2)
  11. Set ws_regeneration2 = Worksheets("Régénération (2)")
  12. ws_regeneration.Copy After:=Sheets(3)
  13. Set ws_regeneration3 = Worksheets("Régénération (3)")
  14. ws_regeneration2.Range(ws_regeneration2.Rows(27), ws_regeneration2.Rows(27).End(xlDown)).ClearContents
  15. ws_regeneration3.Range(ws_regeneration3.Rows(1), ws_regeneration3.Rows(3)).Insert Shift:=xlDown
  16. ws_regeneration3.Range(ws_regeneration3.Rows(28), ws_regeneration3.Rows(29)).Copy ws_regeneration3.Rows(1)
  17. ws_regeneration3.Range("A3") = ws_annees.Range("A25").Value
  18. ws_regeneration3.Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
  19. Range("A1:AA3"), Unique:=False
  20. ws_regeneration3.Range(ws_regeneration3.Rows(1212), ws_regeneration3.Rows(1212).End(xlDown)).Copy ws_regeneration2.Rows("27:27")
  21. ws_regeneration2.Name = ws_annees.[A25]
  22. ws_regeneration3.Delete
  23.  
  24. End Sub


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 ...
Expert Programmation

Alors ça, ce n'est pas une bonne idée :
  1. ws_regeneration.Copy After:=Sheets(2)
  2. Set ws_regeneration2 = Worksheets("Régénération (2)" )
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 :
  1. 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 ici 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 ;) 

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

  1. Set ws_regeneration2 = ws_regeneration.Copy(After:=Sheets(2))


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.
Expert Programmation

: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 :

  1. Dim n As Integer
  2. Dim ws_feuillecible_A As Worksheet
  3. Dim ws_feuillecible_B As Worksheet
  4.  
  5. n = 2
  6. ws_feuillesource.Copy After:=Sheets(n)
  7. Set ws_feuillecible_A = Sheets(n + 1)
  8.  
  9. ws_feuillesource.Copy Before:=Sheets(n)
  10. 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 ?

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?

  1. Sub NouveauTest()
  2.  
  3. Dim n As Integer
  4. Dim ws_regeneration As Worksheet
  5. Dim ws_regeneration2 As Worksheet
  6. Dim ws_regeneration3 As Worksheet
  7. Dim ws_annees As Worksheet
  8. Dim derli As Long
  9. Dim i As Long
  10.  
  11. n = Sheets.Count
  12. Set ws_regeneration = Worksheets("Régénération")
  13. Set ws_annees = Worksheets("Année")
  14. ws_regeneration.Copy After:=Sheets(n)
  15. Set ws_regeneration2 = Sheets(n + 1)
  16. ws_regeneration.Copy After:=Sheets(n + 1)
  17. Set ws_regeneration3 = Sheets(n + 2)
  18.  
  19. ws_regeneration2.Range(ws_regeneration2.Rows(27), ws_regeneration2.Rows(27).End(xlDown)).ClearContents
  20. ws_regeneration3.Range(ws_regeneration3.Rows(1), ws_regeneration3.Rows(3)).Insert Shift:=xlDown
  21. ws_regeneration3.Range(ws_regeneration3.Rows(28), ws_regeneration3.Rows(29)).Copy ws_regeneration3.Rows(1)
  22. ws_regeneration3.Range("A3") = ws_annees.Range("A25").Value
  23. ws_regeneration3.Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
  24. Range("A1:AA3"), Unique:=False
  25.  
  26. derli = ws_regeneration3.Columns(1).Find("*", , , , , xlPrevious).Row
  27. For i = derli To 30 Step -1
  28. If ws_regeneration3.Cells(i, 1) = "" Then
  29. ws_regeneration3.Rows(i).Delete
  30. End If
  31. Next
  32.  
  33. ws_regeneration3.Range(ws_regeneration3.Rows(30), ws_regeneration3.Rows(30).End(xlDown)).Copy ws_regeneration2.Rows("27:27")
  34. ws_regeneration2.Name = ws_annees.[A25]
  35. ws_regeneration3.Delete
  36.  
  37. End Sub



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...
Expert Programmation

PS:

Oui, c'est bon.
Mais tu peux aussi faire comme ça :
  1. ws_regeneration.Copy After:=Sheets(Sheets.Count)
  2. Set ws_regeneration2 = Sheets(Sheets.Count)
  3. ws_regeneration.Copy After:=Sheets(Sheets.Count)
  4. 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 :
  1. Function SheetExists(ByVal name As String, Optional wb As Workbook) As Boolean
  2. Dim sheet As Object
  3.  
  4. If IsMissing(wb) Or wb Is Nothing Then Set wb = ActiveWorkbook
  5.  
  6. For Each sheet In wb.Sheets
  7. If sheet.name = name Then
  8. SheetExists = True
  9. Exit For
  10. End If
  11. Next
  12. End Function
  1. Dim zone As Range
  2. Dim cell As Range
  3. Dim annees() As Boolean
  4. Dim annee_min As Integer
  5. Dim annee_max As Integer
  6. Dim annee As Integer
  7.  
  8. Dim ws_source As Worksheet
  9.  
  10. Set ws_source = Worksheets("xxxxxxxxx")
  11.  
  12. Set zone = ws_source.Range("A2:A31")
  13.  
  14. MsgBox "La zone prise en compte est : " & zone.Address
  15.  
  16. annee_min = WorksheetFunction.Min(zone)
  17. annee_max = WorksheetFunction.Max(zone)
  18.  
  19. MsgBox "L'année la plus ancienne est " & annee_min & ", et la plus récente est " & annee_max
  20.  
  21. ReDim annees(annee_min To annee_max)
  22.  
  23. For Each cell In zone
  24. If Not IsEmpty(cell.Value) And IsNumeric(cell.Value) Then
  25. If Not annees(CInt(cell.Value)) Then MsgBox "Prise en compte de l'année " & CInt(cell.Value)
  26. annees(CInt(cell)) = True
  27. End If
  28. Next
  29.  
  30. For annee = annee_min To annee_max
  31. If annees(annee) And Not SheetExists(CStr(annee)) Then
  32. ws_source.Copy , Sheets(Sheets.Count)
  33. Sheets(Sheets.Count).name = CStr(annee)
  34. End If
  35. Next

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 :

  1. For Each cell In zone
  2. If Not IsEmpty(cell.Value) And IsNumeric(cell.Value) Then
  3. If Not annees(CInt(cell.Value)) Then MsgBox "Prise en compte de l'année " & CInt(cell.Value)
  4. annees(CInt(cell)) = True
  5. End If
  6. Next


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? :( 


Expert Programmation

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 ;)  )

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:

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


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

  1. derli = ws_regene.Columns(1).Find("*", , , , , xlPrevious).Row
  2. For i = derli To 27 Step -1
  3. If ws_regene.Cells(i, 1) <> 2004 Then
  4. ws_regene.Rows(i).Delete
  5. End If
  6. Next


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 ;) 
Expert Programmation

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

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

zeb a dit :
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 ;) 


Effectivement ça change les choses de désactiver le ScreenUpdating... :) 

zeb a dit :
Bien vu. ;) 

C'est donc résolu ?


je n'ai pas encore eu le temps de mepencher réellement sur les bouts de code que tu m'as proposé et de les adapter précisément à mon cas depuis mon dernier post.

Je te donne des nouvelles dès que j'ai fait ça (très bientôt j'espère) !

ça y est me revoilà! :p 

Bon ben j'ai pu me pencher ce week end sur ce dont tu m'avais parlé et j'avoue que je sèche :heink:  sur la façon dont je dois inclure tes bouts de code dans ce que j'ai déjà fait (cf. post précédent).

D'une part, ce qui me gène c'est qu'au final il va y avoir 3 boucles il me semble:

- les 2 tiennes :

  1. For Each cell In zone
  2. If Not IsEmpty(cell.Value) And IsNumeric(cell.Value) Then
  3. If Not annees(CInt(cell.Value)) Then MsgBox "Prise en compte de l'année " & CInt(cell.Value)
  4. annees(CInt(cell)) = True
  5. End If
  6. Next

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

- et celle que j'ai fait pour supprimer les lignes qui ne m'intéressent pas :
  1. For i = derli To 30 Step -1
  2. If ws_regeneration3.Cells(i, 1) = "" Then
  3. ws_regeneration3.Rows(i).Delete
  4. End If
  5. Next

Et là je me dis: ne faudrait-il pas que ces boucles soient imbriquées les unes dans les autres????

Ensuite ce qui me perd c'est ce qui se rapporte à la copie de ma feuille ws_regen.
Normalement pour chaque année que je veux traiter, je la copie 2 fois:
- une fois pour la vider de ces données ws_regen2, ok?
- une seconde fois (ws_regen3), pour y faire subir le filtre et copier les valeurs qui restent alors vers ws_regen2.

Sauf que avec ton code je ne vois pas comment faire pour que ce processus ait lieu pour chaque année.

En fait j'ai beau me retourner le cerveau, je ne comprends pas comment intégrer le ws_source.Copy , Sheets(Sheets.Count) (en fait pour moi ws_regen.Copy , Sheets(Sheets.Count)) qui apparait dans ta dernière boucle.

Chez moi ça donne ça et bien évidemment ça tourne pas...
  1. Sub NouveauTest()
  2.  
  3. Function SheetExists(ByVal name As String, Optional wb As Workbook) As Boolean
  4. Dim sheet As Object
  5.  
  6. If IsMissing(wb) Or wb Is Nothing Then Set wb = ActiveWorkbook
  7.  
  8. For Each sheet In wb.Sheets
  9. If sheet.name = name Then
  10. SheetExists = True
  11. Exit For
  12. End If
  13. Next
  14. End Function
  15.  
  16. Dim n As Integer
  17. Dim ws_regen As Worksheet
  18. Dim ws_regen2 As Worksheet
  19. Dim ws_regen3 As Worksheet
  20. Dim ws_annees As Worksheet
  21. Dim zone As Range
  22. Dim cell As Range
  23. Dim annees() As Boolean
  24. Dim annee_min As Integer
  25. Dim annee_max As Integer
  26. Dim annee As Integer
  27. Dim derli As Long
  28. Dim i As Long
  29.  
  30. Set ws_regen = Worksheets("Régénération")
  31. Set ws_annees = Worksheets("Année")
  32. ws_regen.Copy After:=Sheets(Sheets.Count)
  33. Set ws_regen2 = Sheets(Sheets.Count)
  34. ws_regen.Copy After:=Sheets(Sheets.Count)
  35. Set ws_regen3 = Sheets(Sheets.Count)
  36.  
  37. Set zone = ws_annees.Range("A2:A31")
  38.  
  39. MsgBox "La zone prise en compte est : " & zone.Address
  40.  
  41. annee_min = WorksheetFunction.Min(zone)
  42. annee_max = WorksheetFunction.Max(zone)
  43.  
  44. MsgBox "L'année la plus ancienne est " & annee_min & ", et la plus récente est " & annee_max
  45.  
  46. ReDim annees(annee_min To annee_max)
  47.  
  48. For Each cell In zone
  49. If Not IsEmpty(cell.Value) And IsNumeric(cell.Value) Then
  50. If Not annees(CInt(cell.Value)) Then MsgBox "Prise en compte de l'année " & CInt(cell.Value)
  51. annees(CInt(cell)) = True
  52. End If
  53. Next
  54.  
  55. ws_regen2.Range(ws_regen2.Rows(27), ws_regen2.Rows(27).End(xlDown)).ClearContents
  56. ws_regen3.Range(ws_regen3.Rows(1), ws_regen3.Rows(3)).Insert Shift:=xlDown
  57. ws_regen3.Range(ws_regen3.Rows(28), ws_regen3.Rows(29)).Copy ws_regen3.Rows(1)
  58. ws_regen3.Range("A3") = ws_annees.Range("A25").Value
  59. ws_regen3.Range("A28:AA2036").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _
  60. Range("A1:AA3"), Unique:=False
  61.  
  62. derli = ws_regen3.Columns(1).Find("*", , , , , xlPrevious).Row
  63.  
  64. For i = derli To 30 Step -1
  65. If ws_regen3.Cells(i, 1) = "" Then
  66. ws_regen3.Rows(i).Delete
  67. End If
  68. Next
  69.  
  70. For annee = annee_min To annee_max
  71. If annees(annee) And Not SheetExists(CStr(annee)) Then
  72. ws_regen.Copy , Sheets(Sheets.Count)
  73. Sheets(Sheets.Count).name = CStr(annee)
  74. End If
  75. Next
  76.  
  77. ws_regen3.Range(ws_regen3.Rows(30), ws_regen3.Rows(30).End(xlDown)).Copy ws_regen2.Rows("27:27")
  78. ws_regen2.name = ws_annees.[A25]
  79. ws_regen3.Delete
  80.  
  81. End Sub


Bon en gros c'est le bordel parce que je vois vraiment pas qu'est ce qu'il faut mettre où..... (j'essaie pourtant!)

Lassé par la pub ? Créez un compte