Detection de mots et gestion de position VBA
Résolueriiic Messages postés 25847 Date d'inscription Statut Contributeur Dernière intervention -
J'ai une colonne dont chaque cellule contient un seul ou plusieurs ingrédients, par exemple: Sodium Citrate;Sodium percarbonate;Sodium Carbonate;Sodium Chloride;Sodium Metasilicate;Polyaspartic acid;Alcohol Ethoxylate;Iminodisuccinic acid;enzymes;Polyvinyl Alcohol.
Je voudrai programmer une macro qui:
1) me fasse une liste de tous les ingrédients dans une autre feuille (sans répéter 2 fois un même ingrédient).
2) compte le nombre de fois que cette ingrédient apparaît en 1ère position , 2ème position, 3ème position, etc.
Le tableau final devra donc comporter plusieurs colonnes: une contenant la liste des ingrédients et une pour chaque position d'apparition. Les difficultés pour moi ici sont: comment faire la liste des ingrédients et comment gère-t-on la reconnaissance de la position par Excel?
Merci d'avance à ceux qui pourront m'aider, ça fait un moment que je patauge...
- Detection de mots et gestion de position VBA
- Trousseau mot de passe iphone - Guide
- Ma position - Guide
- Identifiant et mot de passe - Guide
- Mot de passe administrateur - Guide
- Mot de passe bios perdu - Guide
17 réponses
Le problème porte sur l’extraction d’une liste sans doublons d’ingrédients à partir d’une colonne où chaque cellule peut contenir plusieurs ingrédients séparés par des points-virgules, et le comptage de leur position d’apparition (première, deuxième, etc.).
Les solutions proposées préconisent de splitter chaque cellule avec Split(';'), de nettoyer les espaces éventuels par Trim et d’alimenter un dictionnaire pour constituer la liste unique, tout en comptant les occurrences par position lors de l’enregistrement des éléments dans les colonnes suivantes.
Plusieurs difficultés techniques ont été évoquées, comme la gestion de plages dynamiques, le traitement des cellules vides, les erreurs lors du copier-coller de macros entre fichiers, et les limites liées au nommage des feuilles ou au fait de placer le code dans une feuille plutôt que dans un module.
Des conseils soulignent l’intérêt du dictionnaire pour éviter les doublons et l’importance du nettoyage des chaînes, avec des vérifications d’existence et des traitements d’erreurs pour rendre la macro robuste.
peux tu envoyer une partie significative de ton fichier au format excel 2003 sur cjoint.com et joindre le lien obtenu à ton prochain message, ça permettra de voir de façon plus précise comment les données sont organisées
bonne suite
La feuille 1 montre les donnée telles quelles et la feuille 2 est la feuille où les données doivent être placé.
Merci en tout cas de venir à mon aide.
Une proposition :https://www.cjoint.com/?BHktckXtWl8
eric
PS : Je viens de voir que tu as mis ton fichier après que j'ai fait le mien.
Pas encore ouvert mais je pense que tu pourras adapter.
edit: ton fichier adapté avec le total en plus.
https://www.cjoint.com/?BHktBaal0gR
Bon app à ccm81 ;-)
Jamais tu ne répondras à un mp non sollicité...
Bon, ça c'est fait.
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre questionbeau travail, il ne manque que la colonne des nb d'occurences, mais ça ne devrait pas poser de problème à Jack
je vais pouvoir aller diner tranquille ....
j'en étais à la partie (délicate) du dénombrement des positions, je regarderai avec intérêt ta solution demain
cordialement
Je ne peux plus modifier mon post 4
Je viens de voir qu'il y a sans doute un bogue : un total ne peux être égal à 0...
Pas bien grave, je reverrais ça ce we.
Edit : la version corrigée, c'était un oubli suite à l'ajout du total : https://www.cjoint.com/?BHkwR1Jtn2Z
eric
Jamais tu ne répondras à un mp non sollicité...
Bon, ça c'est fait.
Merci beaucoup pour cette aide precieuse, ca a l'air de bien fonctionner. Je suis en train d'essayer de comprendre le programme et il y a une ligne que je comprends pas: c'est quoi CreateObject("Scripting.Dictionary")?
Que dois-je faire?
Sur 2003 : menu 'affichage / barres d'outils / boites à outils contrôles', cliquer sur l'outil 'bouton', cliquer sur la feuille pour le placer.
Puis double-cliquer sur le bouton créé pour accéder à son code. Copier-coller le code entre sub et end sub.
Si pb tu déposer ton fichier sur cjoint.com
eric
Voici mon code adapté:
Option Explicit
Sub Bouton1_Cliquer()
Dim shSource As Worksheet, shListe As Worksheet
Dim plage, c, lig As Long
Dim dico, composition, maxPos As Long
Dim i As Long
Set shSource = Worksheets("Focus Detail")
'
Application.ScreenUpdating = False
Set shListe = Worksheets("Liste")
' nettoyer Liste
shListe.Cells.ClearContents
'
' Passe 1 : liste composants
Set dico = CreateObject("Scripting.Dictionary")
plage = shSource.Range("D2", [D65000].End(xlUp)).Value
For Each c In plage
composition = Split(c, ";")
For i = 0 To UBound(composition)
If Not dico.Exists(Trim(composition(i))) Then dico.Add Trim(composition(i)), 1
Next i
' mémorisation nb max de composants
If UBound(composition) > maxPos Then maxPos = UBound(composition)
Next c
' coller liste
shListe.[a2].Resize(dico.Count, 1) = Application.Transpose(dico.keys)
'
' Passe 2 : position composants
For Each c In plage
' comptage composants par position
composition = Split(c, ";")
For i = 0 To UBound(composition)
Set c = shListe.[a2].Resize(dico.Count, 1).Find(composition(i), LookIn:=xlValues)
shListe.Cells(c.Row, i + 3) = shListe.Cells(c.Row, i + 3) + 1
Next i
'
Next c
'
' total
For lig = 2 To shListe.[a65000].End(xlUp).Row
shListe.Cells(lig, 2) = Application.Sum(shListe.Cells(lig, 3).Resize(1, maxPos))
Next lig
' titres colonnes
shListe.[A1] = "Liste Ingrédient"
shListe.[B1] = "Total"
For i = 1 To maxPos + 1
shListe.Cells(1, i + 2) = "Pos. " & i
Next i
shListe.Rows(1).Font.Bold = True
'
'
Application.ScreenUpdating = True
shListe.Activate
End Sub
https://www.cjoint.com/?BHormnmX5eY
eric
Jamais tu ne répondras à un mp non sollicité...
Bon, ça c'est fait.
C'est l'espace après Beefhide qui perturbait.
J'avais essayé d'anticiper cet éventuel problème avec Trim() mais il en manquait encore un.
https://www.cjoint.com/?BHpaoPDzmY6
eric
Il me semble (voir ton post 2) que tu avais résolu ce problème dans les versions précédente, que faut-il faire?
C'était dû à un autre problème, salt étant présent à l'intérieur d'une autre chaine je l'inscrivais au mauvais endroit.
C'est corrigé.
Comme tu évoquais des pb majuscule/minuscule par sécurité je converti tous les noms en minuscule.
Tu as Alaska Pollock;Alaska pollock;... en position 1 et 2 présents sur 2 lignes.
Avant ils étaient présents séparément sur 2 lignes (avec un des totaux à 0) ce qui pouvait attirer l'oeil sur une anomalie... (dans la mesure où la casse est différente)
Dis si c'est normal ou si c'est une anomalie due au fichier de test qui ne se représentera pas en réel.
Et si je maintiens la conversion en minuscule et donc la fusion des 2 noms.
https://www.cjoint.com/?BHpocZzdcHe
eric
Jamais tu ne répondras à un mp non sollicité...
Bon, ça c'est fait.
Voila je voudrais automatiser ça et que pour chaque Sub-Category ça me crée un onglet ayant le nom de la Sub-Category et contenant la liste et position de ses ingrédients.
Merci
Combien de lignes et de sub-category ?
Processeur, fréquence et taille mémoire ?
En fait tu l'aurais dit tout de suite le problème aurait été abordé différemment.
Là je vais réutiliser ce qui a déjà été fait alors qu'on aurait pu traiter en parallèle toutes les sub-catégories (ce qui obligerait à tout refaire...)
eric
"Si cells(i,3).Value= element j du dictionnaire"
Ce n'est pas comme ça que l'on travaille avec un dictionary.
C'est un tableau de clés (keys) auxquelles sont associées un item.
Tu peux obtenir l'item à partir de la clé : dico.item(clé)
Dans l'exemple que tu demandes il faut utiliser un tableau classique. Beaucoup moins rapide certes, mais toutes les possibilités habituelles d'un tableau.
eric
http://boisgontierjacques.free.fr/pages_site/Dictionnaire.htm
eric
Pourrais-tu y jeter un coup d'oeil s'il te plait?
Voici le lien: http://cjoint.com/?3HrqXgEJsmX
J'ai avancé dans le programme mais j'ai encore quelques problèmes qui bloquent l'exécution.
Déjà, lors de la création de la liste des subcategory (j'ai utilisé un tableau) j'ai remarqué en ajoutant un msgbox, qu'il y avait des répétitions alors que je veux qu'il n'y ait pas de répétition. Je pense que c'est également ce qui bloque le programme par la suite.
Pourrais-tu y jeter un oeil et voir si mon algorithme a une chance de fonctionner s'il te plait?
Voici le fichier: http://cjoint.com/?3Hupaz5DvzY
Merci d'avance
Désolé mais je passerais plus de temps à essayer de corriger ton programme qu'à le faire directement.
De plus sur ton fichier (chez moi) le bouton existe sans exister (?) et le code est impossible à lancer.
Si tu veux une liste sans doublons il est plus efficace d'utiliser un directory quitte à transferer le résult dans un tableau si nécessaire.
Sinon à chaque ajout il faut boucler sur le tableau pour voir si l'élément existe déjà (d'où l'interet du dictionary avec son .exists)
eric
'Liste des Sub-category
ReDim sublist(0)
For i = 2 To [C65000].End(xlUp).Row
For j = 0 To UBound(sublist)
If Not Cells(i, 3).Value = sublist(j) Then
ReDim Preserve sublist(line_insertion)
sublist(line_insertion) = Cells(i, 3).Value
line_insertion = line_insertion + 1
End If
Next j
Next i
N'est-ce pas censé créer une liste des subcategory sans doublons? Ou est l'erreur ici?
PS: je vais jeter un oeil au directory aussi, mais je vois toujours pas pourquoi ca ne marcherait avec un tableau directement.
'Liste des Sub-category
Dim ok As Boolean
ReDim sublist(0)
For i = 2 To [C65000].End(xlUp).Row
ok = True
For j = 0 To UBound(sublist)
If Cells(i, 3).Value = sublist(j) Then
ok = False
Exit For
End If
Next j
If ok Then
ReDim Preserve sublist(line_insertion)
sublist(line_insertion) = Cells(i, 3).Value
line_insertion = line_insertion + 1
End If
Next i
Mais ça sera beaucoup plus lent qu'avec un dictionary.
Tu crées ta liste sans doublon avec, et tu termines avec
Dim liste()
liste = dico.keys
si tu as besoin des fonctionnalités d'un tableau.
eric
Autre question: Comment peut-on tester si un dictionnaire est vide?
J'en ai besoin car comme je le fais pour chaque subcategory, je vide a chaque fois le dico et du coup certaines subcategory ne contenant pas d'ingrédients me font bugger le programme (notamment la partie que tu avais appeler "coller liste").
Dans le for il faut balayer l'ensemble du tableau pour savoir si l'item est déjà présent, pas seulement une seule valeur (ce que tu faisais).
Si on trouve on force une sortie de la boucle (inutile de continuer) et ok = false.
Si ok = true il faut ajouter le nouvel élément.
Comment peut-on tester si un dictionnaire est vide?
Idem que pour un tableau : .count
eric
Je n'arrive vraiment pas a trouver qu'est-ce qui cloche, voici le fichier: http://cjoint.com/?3HvloYkcKfL
Merci
"la fonction Range de_Worksheet a échoué"
Ta feuille shSource doit être activée.
Par ailleurs :
plage = shSource.Range("H2", [H65000].End(xlUp)).Value
me parait plus logique que :
plage = shSource.Range("H1", [H65000].End(xlUp).Offset(1, 0)).Value
Et tu ne gères pas les erreurs sur la création/nommage sur une feuille déjà existante.
Je te conseille de te créer une feuille modèle (qui aura déjà les titres, les largeurs de colonne, etc), de supprimer d'office la feuille à créer en gérant l'erreur si elle n'existe pas, et de créer ta nouvelle feuille à partir de la feuille 'modèle'.
ex :
On Error Resume Next
Application.DisplayAlerts = False
Sheets(sublist(p)).Delete
On Error GoTo 0
Application.DisplayAlerts = True
Sheets("Modèle").Copy After:=Sheets(Sheets.Count)
ActiveSheet.Name = sublist(p)
(je n'ai pas regardé dans le détail ton code, il y a peut-être une adaptation à faire. Sur l'emplacement dans le code par exemple)
eric
"Et tu ne gères pas les erreurs sur la création/nommage sur une feuille déjà existante. "
Je ne comprends pas vraiment ce que tu veux dire... c'est quoi ces erreurs? Ou dois-je insérer ton bout de code?
De plus, maintenant j'ai une erreur 400 lors de l'exécution, c'est peut-être du a ce que tu mentionnes?
Voici le fichier: http://cjoint.com/?3Hvon3Fd3Iv
Encore une fois, merci beaucoup pour ton aide.
Je ne comprends pas vraiment ce que tu veux dire... c'est quoi ces erreurs?
Si une feuille est existante, tu ne peux pas réutiliser le même nom, ça génère une erreur.
Actuellement il faut supprimer manuellement toutes les feuilles crées avant de lancer la macro...
erreur 400
Des tests (pas trouvé de docu là dessus) m'ont permis de voir qu'un nom d'onglet est limité à 31 caractères.
"Boxed Meats - Family Size Frozen" en fait 32.
eric
Par contre maintenant il me dit "incompatibilité de type" pour l'onglet "Boxed Meats - Family Size Frozen" justement, je ne vois pas en quoi il est différent des autres...
Un autre problème que j'avais oublie de mentionner, il y a des onglets ou "Pos." arrête de s'inscrire au bout d'un moment (voir onglet 2 et 4), alors que pour l'onglet 2 ca marche. Il y a pourtant la condition " If UBound(composition) > maxPos Then maxPos = UBound(composition)", qu'est-ce qui manque?
Voila le fichier: http://cjoint.com/?3HvpAZ2owIn
For Each c In plage2
If Not IsError(c) Then
If c <> "" Then
composition = Split(c, ";")
For i = 0 To UBound(composition)
If Not dico.Exists(LCase(Trim(composition(i)))) Then
dico.Add LCase(Trim(composition(i))), 1
Else
dico.Item(LCase(Trim(composition(i)))) = dico.Item(LCase(Trim(composition(i)))) + 1
End If
Next i
' mémorisation nb max de composants
If UBound(composition) > maxPos Then maxPos = UBound(composition)
End If
End If
Next c