• Aucun résultat trouvé

La recherche de valeur peut sembler assez triviale dans l'absolu, pourtant elle peut vite se révéler problématique selon les cas. En effet, en première analyse, on a toujours tendance à penser que la méthode Find correctement utilisée répond à tous les problèmes, or tel n'est pas le cas. La méthode Find présente plusieurs inconvénients.

¾ Elle peut être sensible au type de données, ainsi un code de recherche de date peut échouer si la date est sous forme de chaîne ou sous forme de date

¾ Elle peut être limitée si la valeur cherchée apparaît de nombreuses fois, selon la forme du résultat qu'on souhaite renvoyer.

¾ Elle est souvent assez lente.

Il ne faut pas pour autant la mépriser, elle répond correctement à la plupart des cas, encore faut il la manipuler correctement.

Pour voir les diverses solutions envisageables, repartons d'un exemple classique.

Il n'y a pas de sens aux exemples que je vais prendre, ceux-ci sont justes des exercices de manipulations.

Nous voulons récupérer l'ensemble des cellules de la plage de données qui contiennent la valeur 20.

La première chose à faire généralement est de savoir si celle-ci existe au moins une fois dans la plage de recherche. Ensuite de quoi nous allons chercher à regrouper toutes les cellules qui contiennent cette valeur dans un objet Range. Nous utiliserons pour cela une fonction la plus universelle possible.

Public Sub TestRecherche()

Dim MaPlage As Range, PlageReponse As Range, Zone As Range, Valeur As

Variant

Dim Message As String

Valeur = 20

Set MaPlage = ThisWorkbook.Worksheets(1).UsedRange

Set PlageReponse = PlageValeur(20, MaPlage)

If Not PlageReponse Is Nothing Then

For Each Zone In PlageReponse.Areas

Message = Message & Zone.Address(False, False) & vbNewLine

Next

Else

Message = "La valeur " & Valeur & " n'existe pas dans la plage " &

MaPlage.Address(False, False)

Public Function PlageValeur(ByVal ValeurCherchee As Variant, ByVal

PlageRecherche As Range) As Range

Dim TrouveCell As Range, PremAdresse As String

If Application.WorksheetFunction.CountIf(PlageRecherche,

ValeurCherchee) = 0 Then Exit Function

Set TrouveCell = PlageRecherche.Find(What:=ValeurCherchee,

LookAt:=xlWhole, MatchCase:=False)

PremAdresse = TrouveCell.Address

Set PlageValeur = TrouveCell

Do

Set TrouveCell = PlageRecherche.FindNext(TrouveCell)

Set PlageValeur = Application.Union(PlageValeur, TrouveCell)

Loop Until TrouveCell.Address = PremAdresse

End Function

Ce qui dans notre exemple renverra :

Ce code est relativement efficace sauf si la valeur apparaît dans plusieurs milliers de cellules. Nous pourrions cependant concevoir une autre approche assez différente, mais nettement plus rapide travaillant elle par l'élimination de cellules. En effet, la méthode ColumnDifferences par exemple permet de trouver toutes les cellules d'une colonne ne contenant pas la valeur de la cellule désignée. Or par exclusion, cela revient au même que la recherche de valeur. Nous pourrions donc écrire une fonction telle que :

Public Function PlageValeur1(ByVal ValeurCherchee As Variant, ByVal

Dim Colonne As Range, TrouveCell As Range, NotEqual As Range

If Application.WorksheetFunction.CountIf(PlageRecherche,

ValeurCherchee) = 0 Then Exit Function

For Each Colonne In PlageRecherche.Columns

If Application.WorksheetFunction.CountIf(Colonne, ValeurCherchee) >

0 Then

Set TrouveCell = Colonne.Find(What:=ValeurCherchee,

LookAt:=xlWhole, MatchCase:=False)

Set NotEqual = Colonne.ColumnDifferences(TrouveCell)

NotEqual.EntireRow.Hidden = True

If PlageValeur1 Is Nothing Then

Set PlageValeur1 = Colonne.SpecialCells(xlCellTypeVisible)

Else

Set PlageValeur1 = Application.Union(PlageValeur1,

Colonne.SpecialCells(xlCellTypeVisible)) End If NotEqual.EntireRow.Hidden = False End If Next Colonne End Function

Evidemment ce code est un peu étrange. Comment fonctionne-t-il ?

Je vais travailler en colonne, mais dans l'absolu il faudrait détecter la dimension la moins grande pour travailler dans ce sens. Le code parcours les colonnes de la plage de recherche, à chaque colonne il vérifie s'il existe au moins une fois la valeur cherchée dans la colonne. Si tel est le cas, il appelle la méthode ColumnDifferences qui renvoie la plage de toutes les cellules ne contenant pas la valeur cherchée. Il masque alors cette plage et récupère la plage de cellules visible qui par la force des choses contiennent la valeur cherchée. Il restaure ensuite la visibilité des cellules. Bien que curieuse, cette méthode de recherche est extrêmement rapide.

Ces deux méthodes n'en ont pas moins un inconvénient, elles ne fonctionnent que pour la recherche d'une valeur. Si on recherche plusieurs valeurs, une plage de valeur, une inégalité et ainsi de suite, ça ne marche plus.

Nous allons donc travailler avec une méthode beaucoup plus puissante qui permet de gérer de nombreux cas de recherche, le filtrage. Vous allez voir que dans le principe, c'est la même chose que le code précédent.

Il existe une méthode de filtrage avancée beaucoup plus puissante que nous n'utiliserons pas ici car elle est un peu complexe, nous nous contenterons d'utiliser la méthode AutoFilter qui déjà devrait répondre à bien des attentes.

Commençons par le cas identique de la recherche de valeur.

Public Function PlageValeur2(ByVal ValeurCherchee As Variant, ByVal

PlageRecherche As Range) As Range

Dim Colonne As Range, TrouveCell As Range, NotEqual As Range

If Application.WorksheetFunction.CountIf(PlageRecherche,

ValeurCherchee) = 0 Then Exit Function

For Each Colonne In PlageRecherche.Columns

If Application.WorksheetFunction.CountIf(Colonne, ValeurCherchee) >

0 Then

Colonne.AutoFilter 1, ValeurCherchee

'si ligne de titre

PlageRecherche.Cells(1).EntireRow.Hidden = True

If PlageValeur2 Is Nothing Then

Set PlageValeur2 = Colonne.SpecialCells(xlCellTypeVisible)

Else

Set PlageValeur2 = Application.Union(PlageValeur2,

Colonne.SpecialCells(xlCellTypeVisible)) End If Colonne.AutoFilter 'si ligne de titre PlageRecherche.Cells(1).EntireRow.Hidden = False End If Next Colonne End Function

A part le fait de masquer la ligne de titre pour ne pas comptabiliser de mauvaises cellules, ce code est tout à fait similaire au précédent. L'avantage de cette technique est que sans modification, elle permet des recherches beaucoup plus évoluées. En effet, pour rechercher les valeurs supérieures à 20, il suffit de passer comme argument ">20" à la fonction. On peut d'ailleurs aller plus loin puisqu'on peut aussi passer un critère de recherche de chaîne du type "*Ins" pour toutes les chaînes finissant par 'ins'.