[Résolu] For each ne fait pas tout le range

  • Auteur de la discussion baal28
  • Date de début

baal28

Nouveau membre
Bonjour a tous,
Je suis nouveau sur le forum, donc je vous souhaite bonne année a tous en retard beaucoup :pt1cable:

Voici mon problème de macro qui ne fonctionne pas bien. J'ai une liste de produits sérialise indiquant leur état (deinstall, Bad ou stock). La macro que j'utilise pour expédiez les pièces supprime la ligne et l'envoie sur une autres feuille avec (waybill, 3 facture, # bdt etc...)

Bref ca ne fonctionne pas bien car si j'ai 10 produits a expédiez qui sont identifier deinstall ca n'en prend que 5 et je suis obliger d'exécuter a nouveau la macro et cette fois n'en prend que 2. Une image vaut mille mots ... a vous de voir:
Code:
Sub Deinstall1()
Dim d1 As Range
Dim waybill As String
waybill = InputBox("# Waybill")
For Each d1 In Worksheets("produits").Range("B2:" & ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Address)
  If d1.Value = "Deinstall" Then
    If d1.Offset(0, 5).Value = "DecisionOne" Then
      Worksheets("shipped").Range("A2").EntireRow.Insert
      Worksheets("shipped").Range("A2") = d1.Offset(0, 5)
      Worksheets("shipped").Range("B2") = d1
      Worksheets("shipped").Range("C2") = d1.Offset(0, 1)
      Worksheets("shipped").Range("D2") = d1.Offset(0, 2)
      Worksheets("shipped").Range("E2") = d1.Offset(0, 3)
      Worksheets("shipped").Range("F2") = d1.Offset(0, 4)
      Worksheets("shipped").Range("G2") = waybill
      Worksheets("shipped").Range("H2") = Now
      d1.EntireRow.Delete
    End If
  End If
Next d1
End Sub
 

zeb

Modérateur
Et encore un qui ne sait pas supprimer une ligne dans un tableau !

Par ailleurs, très zoli code. Bien que la ligne 5 contienne du ActiveSheet du plus mauvais goût : Et si la feuille active n'est pas Worksheets("produits") ?
Que dis-tu de ça, plutôt :
Code:
 For Each d1 In Range(Worksheets("produits" ).Range("B2"), _
                      Worksheets("produits" ).Range("B2").SpecialCells(xlCellTypeLastCell))
Tu n'en dis rien, parce que For .. Each n'est pas adapté à une suppression.
Tout est expliqué . ;)

Publie ta solution, si tu trouves. Si tu ne trouves pas, resollicite-nous.
 

baal28

Nouveau membre
Super merci beaucoup zeb... quoique je ne suis pas encore un :na: Maintenant je sais ... comment faire :)

Code:
Sub Deinstall1()
Dim d1 As Range
Dim i As Integer
Dim waybill As String
waybill = InputBox("# Waybill")
For Each d1 In Range(Worksheets("produits").Range("B2"), Worksheets("produits").Range("B2").SpecialCells(xlCellTypeLastCell))
  If d1.Value = "Deinstall" Then
    If d1.Offset(0, 5).Value = "DecisionOne" Then
      Worksheets("shipped").Range("A2").EntireRow.Insert
      Worksheets("shipped").Range("A2") = d1.Offset(0, 5)
      Worksheets("shipped").Range("B2") = d1
      Worksheets("shipped").Range("C2") = d1.Offset(0, 1)
      Worksheets("shipped").Range("D2") = d1.Offset(0, 2)
      Worksheets("shipped").Range("E2") = d1.Offset(0, 3)
      Worksheets("shipped").Range("F2") = d1.Offset(0, 4)
      Worksheets("shipped").Range("G2") = waybill
      Worksheets("shipped").Range("H2") = Now
    End If
  End If
Next d1
For i = Application.WorksheetFunction.CountA(Range("produits!A1:A65536")) To 2 Step -1
  If Range("b" & i).Value = "Deinstall" And Range("g" & i).Value = "DecisionOne" Then Rows(i).Delete
Next i
End Sub
 

baal28

Nouveau membre
J'ai un autre petit problème avec ce code dans la condition
Code:
If d1.Value = "Deinstall"
et
Code:
If d1.Offset(0, 5).Value = "DecisionOne"

Si la case rechercher est écris deinstall ou decisionone ca ne fonctionne pas a cause des majuscules... n'y aurait il pas une facon simple de corriger le problème. Peut etre en marquant .text au lieu de .value ?
 

zeb

Modérateur
Pour ton dernier problème, et quelque soit le langage, voici la solution, évidente quand tu l'auras lue :
Code:
Si Majuscule ( Variable ) = "VALEUR" Alors ...
Si Minuscule ( Variable ) = "valeur" Alors ...
Si Majuscule ( Variable1 ) = Majuscule ( Variable2 ) Alors ...
Si Minuscule ( Variable1 ) = Minuscule ( Variable2 ) Alors ...

Mais peut-être as-tu déjà trouvé, le temps que je te réponde.
_______________________________________________________________________

Quant à ta solution, quelle horreur. Je ne parle pas de la maîtrise du langage, mais de l'algorithme. Pouah !!
En plus il reste des erreurs.

Tu l'as ta zone, tu la définis ligne 7 ! Qu'est-ce que tu nous fais ligne 21 ?
Je te rappelle que CountA c'est la fonction NBVAL, et que si tu as un trou dans ta liste, ta boucle est fausse.
Et puis pourquoi deux boucles. Une seule suffit !

Et encore une remarque sous forme de devinettes :
■ Combien y-a-t-il de ligne dans une feuille XL ?
■ Entre quelles valeurs, la valeur d'un entier de type Integer est-elle comprise ?

Ah, j'oubliais. Partout, tu précises la feuille sur laquelle tu travailles. C'est très bien. Comme ça, pas de select, d'activate ou de ce genre de choses qui ralentissent le programme ou qui supposent que l'on doit être dans tel cas particulier pour que ça marche. Sauf que ligne 22, tu l'oublies ! (En plus, c'est au moment de la suppression des lignes. Si tu veux perdre tes données, c'est comme ça qu'il faut faire).

Regarde :
Code:
Dim ws_s As  Worksheet
Dim ws_p As  Worksheet
Dim rg_s As  Range
Dim rg_p As  Range
Dim r        As Long
Dim last_row As Long

Set ws_s = Worksheets("shipped" )
Set ws_p = Worksheets("produits" )

' // S'il ne doit pas y avoir de trou entre la première valeur et la dernière :
last_row = ws_p.Range("B2" ).End(xlDown).Row
' // S'il peut y avoir des trous :
last_row = ws_p.Range("B65536" ).End(xlUp).Row

For r = last_row To 2 Step -1
  If Lcase(ws_p.Range("B" & r).Value) = "deinstall" And _
     UCase(ws_p.Range("G" & r).Value) = "DECISIONONE" Then
      ws_s.Range("A2").EntireRow.Insert

      Set rg_p = ws_p.Range("B" & r)
      Set rg_s = ws_s.Range("A2")

      ws_s.Range("A2").Value = rg_p.Offset(, 5).Value
      ws_s.Range("B2").Value = rg_p.Offset(, 0).Value
      ws_s.Range("C2").Value = rg_p.Offset(, 1).Value
      ws_s.Range("D2").Value = rg_p.Offset(, 2).Value
      ws_s.Range("E2").Value = rg_p.Offset(, 3).Value
      ws_s.Range("F2").Value = rg_p.Offset(, 4).Value
      ws_s.Range("G2").Value = waybill
      ws_s.Range("H2").Value = Now

      rg_p.EntireRow.Delete
  Enf If
Next
Bon, c'est pas mal, qu'est-ce que t'en penses ?

Moi, je suis un gros fainéant. Je n'aime pas écrire six fois la même chose.
Alors je réécris les lignes 22 à 29 :
Code:
Set rg_s = ws_s.Range("A2")

rg_s.Offset(, 0).Value = rg_p.Offset(, 5).Value
rg_s.Offset(, 1).Value = rg_p.Offset(, 0).Value
rg_s.Offset(, 2).Value = rg_p.Offset(, 1).Value
rg_s.Offset(, 3).Value = rg_p.Offset(, 2).Value
rg_s.Offset(, 4).Value = rg_p.Offset(, 3).Value
rg_s.Offset(, 5).Value = rg_p.Offset(, 4).Value

Code:
Set rg_s = ws_s.Range("A2")
For i = 0 To 5                     
  rg_s.Offset(, i) = rg_p.Offset(, (i + 5) Mod 6 )
Next
rg_s.Offset(, 6).Value = waybill
rg_s.Offset(, 7).Value = Now

Une dernière chose.
La méthode Range peut accepter une chaîne de caractères et on peut construire l'adresse comme ceci :
Code:
rg_p = ws_p.Range("B" & r)
Ce qui est plus sympa, à première vue, que cela :
Code:
rg_p = ws_p.Cells(r, 2)
Mais s'habituer à la notation (Ligne,Colonne) a l'avantage d'être plus facile à traiter avec des nombres, dans des boucles, par exemple. La preuve, c'est que tant que c'est la ligne qui varie, c'est sympa, mais si c'était sur la colonne qu'il fallait itérer, ferais-tu des choses comme ça :
Code:
For colonne = 1 To 10
  Range(Chr(colonne+64)) ..
Next
Surtout pas, parce que le suivant de 'Z', c'est 'AA' pour Excel, et non pas '[' pour l'Ascii.


On reprend le tout :
Code:
Dim ws_s As  Worksheet
Dim ws_p As  Worksheet
Dim rg_s As  Range
Dim rg_p As  Range
Dim r        As Long
Dim i        As Long
Dim last_row As Long

Set ws_s = Worksheets("shipped")
Set ws_p = Worksheets("produits")

' // S'il ne doit pas y avoir de trou entre la première valeur et la dernière :
last_row = ws_p.Cells(2, 2).End(xlDown).Row
' // S'il peut y avoir des trous :
last_row = ws_p.Cells(65536, 2).End(xlUp).Row

For r = last_row To 2 Step -1
  If Lcase(ws_p.Cells(r, 2).Value) = "deinstall" And _
     UCase(ws_p.Cells(r, 7).Value) = "DECISIONONE" Then
      ws_s.Cells(2, 1).EntireRow.Insert

      Set rg_p = ws_p.Cells(r, 2)
      Set rg_s = ws_s.Cells(2, 1)

      For i = 0 To 5                     
        rg_s.Offset(, i) = rg_p.Offset(, (i + 5) Mod 6)
      Next
      
      rg_s.Offset(, 6).Value = waybill
      rg_s.Offset(, 7).Value = Now

      rg_p.EntireRow.Delete
  Enf If
Next

TADAAA!
 

baal28

Nouveau membre
Tu déchires zeb!! ABRACADABRA ... C'est magique! Ca fonctionne #1 et beaucoup plus rapide. Merci beaucoup

Tu as tout fais pour moi avec des explication en plus WAW :) Il y a quand meme une ligne que je comprend pas trop, ca fais une semaine que je me suis au VBA, anciennement mirc scripting alors j'en arrache encore.

Code:
       For i = 0 To 5                   
         rg_s.Offset(, i) = rg_p.Offset(, (i + 5) Mod 6)
       Next

mod6 c'est quoi au juste ?
 

zeb

Modérateur
Bon, je t'avoue que je m'emmerde un peu au boulot. Et pis ce n'est pas mon habitude que de tout donner tout fait. Par contre, tu semblais avoir de très bonnes bases (le langage en lui-même est bien utilisé je trouve, c'est rare avec le VBA) et je sentais que tu allais lire et comprendre ce que je te proposais avant de l'accepter et de l'utiliser, au contraire de certains qui l'auraient pris sans se poser de questions. J'ai la preuve que tu l'as lu, puisque tu poses une question pertinente. Ça fait plaisir :)

Spa de la programmation, ce sont des maths !

Code:
rg_s.Offset(, 0).Value = rg_p.Offset(, 5).Value
rg_s.Offset(, 1).Value = rg_p.Offset(, 0).Value
rg_s.Offset(, 2).Value = rg_p.Offset(, 1).Value
rg_s.Offset(, 3).Value = rg_p.Offset(, 2).Value
rg_s.Offset(, 4).Value = rg_p.Offset(, 3).Value
rg_s.Offset(, 5).Value = rg_p.Offset(, 4).Value
Quand un code ressemble à ça, moi, je ne vois que ça :
[fixed]0 -> 5
1 -> 0
2 -> 1
3 -> 2
4 -> 3
5 -> 4[/fixed]Et je me demande quelle fonction f associe 0 à 5, 1 à 0, 2 à 1, etc.

Ben figure-toi que la fonction[fixed]f : N -> N
f(x) = (x + 5) - (x + 5) / 6[/fixed]correspond très bien.

Rappelle-toi ce qu'est une division entière : c'est un dividende A, un divideur B, un quotient Q, et un reste R.
[fixed]. A | B
... +--
. R | Q[/fixed]

Et bien en mathématique, il existe une fonction qui associe (A,B) à R, on l'appelle Modulo.

La fonction f s'écrit :[fixed]f : N -> N
f(x) = (x + 5) Modulo 6[/fixed]

La fonction Modulo est très pratique pour les permutations circulaires.
En VB, elle s'écrit Mod, en C/C++, c'est %.
 

baal28

Nouveau membre
Aaaah j'ai juste pas été a l'école assez longtemps pour connaitre la fonction modulo. T'es un bon professeur, j'ai tout compris ton code maintenant. Ca m'énerve faire quelque chose sans savoir pourquoi. Je t'apporte une pomme demain... promis ;)

Et de plus ce n'est qu'une partie de mon code que j'ai apporter ici, donc en comprennent parfaitement ce code très optimisé je devrais être en mesure de optimiser le reste de mon code, qui doit comporter encore des bugs importants.

Histoire de protéger mon orgueil démesurer, je vais tenter de pas écrire un nouveau sujet concernant le reste de mon code. Merci beaucoup zeb !!!
 

zeb

Modérateur
T'est un bon élève !
:D

N'hésite pas pas à poser des questions, ou à solliciter un peu d'aide. Avec l'esprit critique que certains ont ici, tu obtiendras en plus d'une solution, des remarques, des critiques, des objections qui peuvent te faire apprendre et progresser. T'avais rien demandé, et pourtant, tu connais maintenant Modulo ;)
 
Vous devez vous inscrire ou vous connecter pour répondre ici.
Derniers messages publiés
Statistiques globales
Discussions
730 136
Messages
6 718 121
Membres
1 586 398
Dernier membre
mookie767
Partager cette page
Haut