none
Chercher dans un array avec uniquement le début du nom [VB] RRS feed

  • Question

  • Bonjour,

    J'ai une liste d'objet

    Public romIndex As New List(Of Games)
    

    Voici classe objet 'game' dans laquelle j'ai intégré une fonction de comparaison basée sur 'descript'.

    Public Class Games
        Public Shared ReadOnly DescriptComparer As New DescriptComparerClass
        Public rom As String
        Public descript As String
        Public genre As String
        Sub New()
        End Sub
        Public Class DescriptComparerClass
            Implements IComparer(Of Games)
            Public Function Compare(ByVal x As Games, ByVal y As Games) As Integer Implements System.Collections.Generic.IComparer(Of Games).Compare
                Return String.Compare(x.descript, y.descript, True)
            End Function
        End Class
    End Class
    

    Ce qui me permet de faire du tri et BinarySearch basé sur 'descript' dans la liste

    romIndex.Sort
    Dim ng As New Games
    ng.descript = "test"
    Dim ndx As Int32 = romIndex.BinarySearch(0, romIndex.Count, ng, Games.DescriptComparer)
    If ndx >= 0 Then
       Debug.Print ("Trouvé")
    End If
    

     

    Alors, voila ma question:
    =================

    Comment pourrais-je faire une recherche dans la liste en partant uniquement des lettres du début du string. Donc pour reprendre l'exemple, trouver dans la liste le premier élément qui commencerait par "Te" (et qu'il me trouve "Test").

    Pour l'instant j'ai simplement une boucle, mais je voudrais quelque chose de plus abouti car cela concerne des dizaines de milliers d'éléments et je sais qu'il doit y avoir plus performant que ma boucle

    For Each ri As Games In romIndex
     If ri.descript.ToUpper.StartsWith(texteàtrouver) Then
      ...
      ...
      Exit For
     End If
    Next
    

    Que puis je utiliser (et comment) qui ait la même "rapidité" qu'un BinarySearch (sur le string complet) avec uniquement le début d'un string?

     

    J'espère que j'ai pu me faire comprendre ;-) et je vous remercie d'avance de l'aide que vous voudrez bien m'apporter

     

    mardi 29 novembre 2011 13:22

Réponses

  • Bonjour,

     

    Le plus facile et le plus court serait d'utiliser Linq.

    Essayer l'instruction suivante :

    Dim foundedGames = romIndex.Select(Function(x) x.descript).FirstOrDefault(Function(x) x.StartsWith("Te"))
    

    Sinon vous devez aussi prêter attention à quelques détails très important comme le nommage : Vos nom de membres publics doivent toujours commencer par une majuscule. Je peux pas en dire plus car je suis plus programmeur C# que VB .NET.

     

    Cordialement.

    mardi 29 novembre 2011 17:45
    Auteur de réponse
  • Je pensais à qq chose comme :

    Module Module2
        Public Class Games
            Public Shared ReadOnly DescriptComparer As New DescriptComparerClass
            Public rom As String
            Public descript As String
            Public genre As String
            Sub New()
            End Sub
            Public Class DescriptComparerClass
                Implements IComparer(Of Games)
                Public Function Compare(ByVal x As Games, ByVal y As Games) As Integer Implements System.Collections.Generic.IComparer(Of Games).Compare
                    Return String.Compare(x.descript, y.descript, True)
                End Function
            End Class
            Public Class DescriptStartsComparerClass
                Implements IComparer(Of Games)
                Public Function Compare(ByVal x As Games, ByVal y As Games) As Integer Implements System.Collections.Generic.IComparer(Of Games).Compare
                    If x.descript.StartsWith(y.descript) Then Return 0
                    Return String.Compare(x.descript, y.descript, True)
                End Function
            End Class
        End Class
        Public Sub Main()
            Dim Data(5) As Games
            Data(0) = New Games With {.descript = "A..."}
            Data(1) = New Games With {.descript = "Test2.."}
            Data(2) = New Games With {.descript = "Test3.."}
            Data(3) = New Games With {.descript = "X..."}
            Data(4) = New Games With {.descript = "Y..."}
            Data(5) = New Games With {.descript = "Z..."}
            Dim ng As New Games
            ng.descript = "Te"
            Dim ndx As Int32 = Array.BinarySearch(Of Games)(Data, ng, New Games.DescriptStartsComparerClass)
            Console.WriteLine(ndx)
            Console.ReadLine()
        End Sub
    End Module
    

    Faire peut-être effectivement une comparaison avec la solution Linq suggérée par Ould Mourad pour vérifier que la recherche binaire apporte un vrai gain par rapport à la solution Linq (qui au final fait une boucle).

    D'un autre côté on a toutes les solutions tandis qu'avec la recherche Binaire on a juste une solution quelconque, il faut encore tester les éléments adjacents pour trouver les autres éléments.

    A terme, il serait peut-être possible d'utiliser une "vraie" base de données selon ce que l'on veut faire.


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    mardi 29 novembre 2011 18:49
    Modérateur
  • A titre purement informatif j'arrive à qq chose comme :

    Dim q As Integer = romIndex.Select(Function(f, index) New With {.Data = f, .index = index}).
                FirstOrDefault(Function(f) f.Data.Descript.StartsWith("5000", StringComparison.CurrentCultureIgnoreCase)).Index
    

    Cela reste plus lent (mais sans différence perceptible) sans doute parce qu'en contrepartie il est nécessaire de créer des types anonymes pour numéroter les éléments. De plus il faut encore gérer le cas un peu spécial ou aucun élément n'est trouvé.

    D'après ce que je vois l'approche précédente avec Linq semble largement suffisante (même avec 1000000 d'élements cela me semble en gros instantané).

    Linq examine à priori tous les éléments car ne sais pas que le tableau est trié. Donc je dirais que le plus simple serait de ne pas même garder la contrainte de tri de ce tableau et de retourner donc la liste des éléments trouvés (et modifié un élement de cette liste modifiera effectivement l'élément présent dans le tableau). 

    Comme je disais précédemment, il est sans doute inutile de se casser la tête ou de s'ajouter des contraintes qui ne sont pas d'actualité maintenant (car elles risquent de ne l'être jamais donc on se complique la vie sans en retirer jamais aucun bénéfice).

    Au final un petit test me donne :

            ' Données de test
            Dim displayArray(1000000) As Displayed
            For i As Integer = 0 To UBound(displayArray)
                displayArray(i) = New Displayed With {.rom = i.ToString, .Descript = i.ToString}
            Next
            Dim s As New Stopwatch
            s.Start()
            ' Recherche ce qui commence par 5000
            Dim foundList As IEnumerable(Of Displayed) = displayArray.Where(Function(o) o.Descript.StartsWith("5000", StringComparison.CurrentCultureIgnoreCase))
            ' Traite les éléments trouvés
            For Each found As Displayed In foundList
                found.Descript = "Z"
                Debug.WriteLine(Array.IndexOf(displayArray, found)) ' On peut récupérer l'index si vraiment nécessaire
            Next
            s.Stop()
            ' Montre que les modifications portent bien sur les éléments stockés dans le tableau
            MessageBox.Show(displayArray(5000).Descript)
            ' Montre le temps écoulé (et pas seulement pour la recherche, le plus long est d'écrire dans la fenêtre de débogage)
            MessageBox.Show(s.ElapsedMilliseconds.ToString)
    
    


    Le tout prends environ 800 ms chez moi et 200 ms environ si je n'écris pas les index vers la fenêtre de débogage.

     


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    jeudi 1 décembre 2011 18:16
    Modérateur
  • Je voudrais tous vous remercier..et notamment Patrice qui m'a suivi de près!

    Patrice, j'ai finalement retenu une de tes premières propositions: faire un BinarySearch, pour se situer très rapidement dans l'array, puis ensuite remonter la liste jusqu'au premier élément!

    On m'a, ici et ailleurs, fait remarquer que les vitesses de traitement étaient de toutes les façons très bonnes...mais c'est plus fort que moi, j'aime me dire que mon code s'exécute très rapidement (sans doute parce que mes début à tripatouiller du basic datent du Commodore 64 ;-) ) et je me dit que je pourrais ainsi m'en resservir dans d'autres occasions où la vitesse serait critique...enfin soit, on ne se refait pas ;-)

    Alors vous allez peut-être sourire des chiffres que je vais vous donner et qui sont dans tous les cas acceptables.
    - Ma boucle de départ, testée en situation sur 5000 éléments donnait +/- 0,030 sec
    - L'utilisation de Linq donnait +/- 0,020 sec
    - L'utilisation du code ci-dessous donne +/- 0,0010 sec

    Voici donc, pour ceux que ça intéresserait, le code final:

     

    Public Shared displayArray As New List(Of Displayed)
    ' Il faudra bien sur remplir ce List (of Displayed), ce n'est pas expliqué ici ' Public Class Displayed Public Shared ReadOnly DescriptComparer As New DescriptComparerClass Public rom As String Public descript As String Public Class DescriptComparerClass 'Utilisé pour permettre une comparaison basée sur la propriété 'descript' (en l'occurence trier l'array) Implements IComparer(Of Displayed) Public Function Compare(ByVal x As Displayed, ByVal y As Displayed) As Integer Implements System.Collections.Generic.IComparer(Of Displayed).Compare Return String.Compare(x.descript, y.descript, True) End Function End Class Public Class DescriptStartsComparerClass 'Utilisé pour faire un BinarySearch sur 'descript Implements IComparer(Of Displayed) Public Function Compare(ByVal x As Displayed, ByVal y As Displayed) As Integer Implements System.Collections.Generic.IComparer(Of Displayed).Compare If x.descript.ToUpper.StartsWith(y.descript) Then Return 0 Return String.Compare(x.descript, y.descript, True) End Function End Class End Class '
    displayArray.Sort(Displayed.DescriptComparer) 'Trie l'array
    displayText = "ba" 'exemple de texte à chercher comme étant le début d'un élément descript Dim ng As New Displayed 'Crée un nouvel objet et lui attribue le texte à chercher comme propriété 'descript' ng.descript = displayText ' Cherche "en gros" un élément contenant le texte et retourne son index
    Dim ndx As Int32 = displayArray.BinarySearch(0, displayArray.Count, ng, New Displayed.DescriptStartsComparerClass) ' Et maintenant, "affinage" en remontant la liste jusqu'au 1er ékément contenant toujours ce mot<br/>If ndx >= 0 Then 'si le binary search n'a pas retourné un '-1' (rien trouvé) If ndx <= 1 Then 'à 1 on ne lance pas la procédure, on fait juste remonter l'index (évite une erreur avec un 'displayArray(-1)' dans la boucle ci-dessous) ndx = 0 Else For i = ndx - 1 To 0 Step -1 If displayArray(i).descript.ToUpper.StartsWith(displayText) Then ndx = i Else Exit For End If Next End If masterIndex = ndx 'variable contenant l'index End If


    Patrice, il y a encore quelque chose qui m'échappe un peu dans ceci:

    Public Function Compare(ByVal x As Displayed, ByVal y As Displayed) As Integer Implements System.Collections.Generic.IComparer(Of Displayed).Compare 
      If x.descript.ToUpper.StartsWith(y.descript) Then Return 0 
      Return String.Compare(x.descript, y.descript, True)
    End Function

    Ce sont les 2 'Return'! Quand je lis qu'un 0 est retourné si la condition est vérifiée, je m'attendrais a plûtot avoir la position dans l'index retournée?! Peux tu m'expliquer, stp, ce que ces return..retournent...et à qui!


    Voila, j'espère que cela pourra aider quelqu'un...et je remercie encore Patrice pour son aide cruciale!!

    • Modifié jmdeb samedi 3 décembre 2011 16:44
    • Marqué comme réponse Ciprian Duduiala lundi 5 décembre 2011 08:38
    samedi 3 décembre 2011 16:24

Toutes les réponses

  • Bonjour,

    Mon premier mouvement serait d'utiliser une autre implémentation qui utiliserait StartsWith plutôt que Compare (avec peut-être une logique pour exploiter les éléments adjacents qui peuvent également correspondre). Avez-vous essayé ?


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    mardi 29 novembre 2011 16:51
    Modérateur
  • J'aimerais pouvoir l'essayer. Malheureusement, en tant que débutant je n'en ai guère les moyens!! Le code repris dans ma question m'a été "soufflé" très généreusement par Foleide...et je ne vois pas, pour l'instant, comment l'adapter avec un StartsWith que tu proposes.

    Je souhaiterais que l'on puisse me prendre plus "par la main"

    • Modifié jmdeb mardi 29 novembre 2011 17:38
    mardi 29 novembre 2011 17:37
  • Bonjour,

     

    Le plus facile et le plus court serait d'utiliser Linq.

    Essayer l'instruction suivante :

    Dim foundedGames = romIndex.Select(Function(x) x.descript).FirstOrDefault(Function(x) x.StartsWith("Te"))
    

    Sinon vous devez aussi prêter attention à quelques détails très important comme le nommage : Vos nom de membres publics doivent toujours commencer par une majuscule. Je peux pas en dire plus car je suis plus programmeur C# que VB .NET.

     

    Cordialement.

    mardi 29 novembre 2011 17:45
    Auteur de réponse
  • Je pensais à qq chose comme :

    Module Module2
        Public Class Games
            Public Shared ReadOnly DescriptComparer As New DescriptComparerClass
            Public rom As String
            Public descript As String
            Public genre As String
            Sub New()
            End Sub
            Public Class DescriptComparerClass
                Implements IComparer(Of Games)
                Public Function Compare(ByVal x As Games, ByVal y As Games) As Integer Implements System.Collections.Generic.IComparer(Of Games).Compare
                    Return String.Compare(x.descript, y.descript, True)
                End Function
            End Class
            Public Class DescriptStartsComparerClass
                Implements IComparer(Of Games)
                Public Function Compare(ByVal x As Games, ByVal y As Games) As Integer Implements System.Collections.Generic.IComparer(Of Games).Compare
                    If x.descript.StartsWith(y.descript) Then Return 0
                    Return String.Compare(x.descript, y.descript, True)
                End Function
            End Class
        End Class
        Public Sub Main()
            Dim Data(5) As Games
            Data(0) = New Games With {.descript = "A..."}
            Data(1) = New Games With {.descript = "Test2.."}
            Data(2) = New Games With {.descript = "Test3.."}
            Data(3) = New Games With {.descript = "X..."}
            Data(4) = New Games With {.descript = "Y..."}
            Data(5) = New Games With {.descript = "Z..."}
            Dim ng As New Games
            ng.descript = "Te"
            Dim ndx As Int32 = Array.BinarySearch(Of Games)(Data, ng, New Games.DescriptStartsComparerClass)
            Console.WriteLine(ndx)
            Console.ReadLine()
        End Sub
    End Module
    

    Faire peut-être effectivement une comparaison avec la solution Linq suggérée par Ould Mourad pour vérifier que la recherche binaire apporte un vrai gain par rapport à la solution Linq (qui au final fait une boucle).

    D'un autre côté on a toutes les solutions tandis qu'avec la recherche Binaire on a juste une solution quelconque, il faut encore tester les éléments adjacents pour trouver les autres éléments.

    A terme, il serait peut-être possible d'utiliser une "vraie" base de données selon ce que l'on veut faire.


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    mardi 29 novembre 2011 18:49
    Modérateur
  • Le plus facile et le plus court serait d'utiliser Linq.

    Essayer l'instruction suivante :

    Dim foundedGames = romIndex.Select(Function(x) x.descript).FirstOrDefault(Function(x) x.StartsWith("Te"))
    

    Sinon vous devez aussi prêter attention à quelques détails très important comme le nommage : Vos nom de membres publics doivent toujours commencer par une majuscule. Je peux pas en dire plus car je suis plus programmeur C# que VB .NET.

    Merci pour votre réponse!
    Cela marche plutôt bien (va 2x plus vite qu'une boucle)
    2 questions, svp:
    - cela me retourne le nom, comment puis-je faire pour que cela me retourne l'index dans l'array, svp?
    - la recherche doit pouvoir se faire en ne tenant pas compte de la casse (majuscule, minuscule), là aussi comment faire, svp?

    D'avance merci!!

    mercredi 30 novembre 2011 10:52
  • Patrice,

    Merci beaucoup pour votre réponse!
    Ce que vous proposez est en effet très rapide (comme on peut s'y attendre avec un BinarySearch).
    Le temps de traitement est divisé par 15 (!!) dans mon cas...c'est donc parfait.

    (1)
    Je ne sais exactement pour quelle raison, mais j'ai du adapter le

    Dim ndx As Int32 = Array.BinarySearch(Of Games)(Data, ng, New Games.DescriptStartsComparerClass)
    

    en

    Dim ndx As Int32 = Data.BinarySearch(0, Data.Count, ng, New Games.DescriptStartsComparerClass)
    

    sinon il me retournait: "Value of type 'System.Collections.Generic.List(Of application.Games)' cannot be converted to '1-dimensional array of application.Games'."

    (2)
    Le seul problème, comme vous le souleviez dès le début, c'est que le résultat n'est pas aussi "net".
    Si l'on tape "t" on arrive de fait sur un des éléments qui commence par "t"...mais pas le premier
    Si l'on tape "te" on arrive sur un élément qui commence par "te"...mais pas le premier.
    On arrive malgré tout, en affinant, à l'élément recherché, mais je me dit que pour l'utilisateur cela peut paraitre un peu "aléatoire" (et ce l'est de fait, je le comprends bien, par le principe même du binary search)

    Ceci étant, voyez vous une façon/procédure pour s'assurer de "remonter" vers le premier élément contenant la requête, dans la liste?



    • Modifié jmdeb mercredi 30 novembre 2011 11:42
    mercredi 30 novembre 2011 11:39
  • Oui, on n'obtient effectivement qu'un seul élement parmi ceux qui correspondent.

    Comme les éléments sont triés on peut donc ensuite :
    - examiner les éléments précédents jusqu'au premier ou jusqu'à ne plus trouver une correspondance
    - examiner les éléments suivants jusqu'au dernier ou jusqu'à ne plus trouver une correspondance

    En code cela doit donner qq chose comme :

    do while minIndex>0 andalso Data(minIndex-1).Descript.StartsWith("Te")
       minIndex-=1
    loop

    do while maxIndex<Data.Length-1 andalso Data(maxIndex+1).Descript.StartWith("Te")
        maxIndex+=1
    loop

    minIndex et maxIndex sont initialisés avec l'index trouvé. Donc après exécution les solutions sont donc tous les élements entre minIndex et maxIndex. Je n'ai pas eu le temps de tester le code donc j'ai pu me planter dans les détails.

    Pour les performances attention aussi à voir la valeur absolue. Un temps de traitement divisé par 15 n'est pas intéressant si on passe 0.15 s à 0.01 s. Un temps de traitement divisé par 2 est intéressant si on passe de 4 à 2 s. Je dis cela parce que l'utlsiation de Linq est plus simple et sera plus proche de ce que l'on ferait avec une "vraie" base de données et donc utiliser la recherche binaire si cela apporte vraiment qq chose de perceptible et non pas si c'est juste plus rapide mais sans que l'utilisateur ne voit la différence...

     

     

     

     

     


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    mercredi 30 novembre 2011 12:10
    Modérateur
  • Patrice,

    Merci pour votre réponse et vos éclaircissement.
    Puis-je encore vous demander si Linq me permettrais de retourner l'index dans l'array et non le nom de la variable?

    Merci

    mercredi 30 novembre 2011 23:44
  • On peut récupérer ce que l'on veut par exemple :

    Dim foundGames=Data.where(fucntion(x) x.StarsWith("Te",StringComparison.CurrentCultureIgnoreCase)) retourne une liste des données qui répondent au critère avec toutes leur priorité (réponse rapide, code non testé)


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    jeudi 1 décembre 2011 10:41
    Modérateur
  • Merci à nouveau pour ta réponse..et désolé "d'insister" mais pour retourner l'INDEX du PREMIER élément, dans une liste TRIEE, dont la valeur commencerait par "Te" quelle serait la syntaxe exacte stp...(j'ai bien cherché par moi-même avant de revenir vers toi...mais je ne trouve pas ce dont j'ai besoin)

    D'avance merci

    jeudi 1 décembre 2011 12:51
  • Pas de souci, je pensais que c'éait plus pratique de récupérer directement la liste des objets concernés par le critère.

    Pour l'index voir http://msdn.microsoft.com/en-us/library/system.array.indexof.aspx

    On aurait qq chose comme :

    Data.IndexOf(Data.FirstOrDefault(Function(x) x.StartsWith("Te",StringComparison.CurrentCultureIgnoreCase)))

    Par contre on recherche le premier élément puis on recherche à nouveau dans cet élément l'index de la ligne.

    Il doit être possible avec linq de générer l'index à la volée ce qui permet de le récupérer directement ce qui pourrait être un peu plus rapide sur une longue liste mais pour cela il faudra que je prenne le temps de tester la syntaxe exacte.


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    jeudi 1 décembre 2011 14:10
    Modérateur
  • Dim foundGames=Data.IndexOf(Data.FirstOrDefault(Function(x) x.StartsWith("Te",StringComparison.CurrentCultureIgnoreCase)))

     Merci (et j'espère que tu auras le temps de trouver comment générer l'index à la volée avec Linq :-)

    En attendant, la ligne ci-dessus retourne un " 'StartsWith' is not a member of 'application.Displayed' "

    Je te remet la structure pour que tu y voies plus clair:

    Public Shared displayArray As New List(Of Displayed)
    '
    Public Class Displayed
        Public Shared ReadOnly DescriptComparer As New DescriptComparerClass
        Public rom As String
        Public descript As String
        Public Class DescriptComparerClass
            Implements IComparer(Of Displayed)
            Public Function Compare(ByVal x As Displayed, ByVal y As Displayed) As Integer Implements System.Collections.Generic.IComparer(Of Displayed).Compare
                Return String.Compare(x.descript, y.descript, True)
            End Function
        End Class
     End Class
    

    Et la ligne adaptée à ce que tu viens de me donner:
    Dim foundGames = displayArray.IndexOf(displayArray.FirstOrDefault(Function(x) x.StartsWith("Te", StringComparison.CurrentCultureIgnoreCase)))
    

    Merci!!!
    • Modifié jmdeb jeudi 1 décembre 2011 15:08
    jeudi 1 décembre 2011 15:04
  • Bon, j'y suis parvenu (grâce à toi) en adaptant la ligne ainsi:

    Dim foundGames = displayArray.IndexOf(displayArray.FirstOrDefault(Function(x) x.descript.StartsWith("Te", StringComparison.CurrentCultureIgnoreCase)))
    


    Ce que je constate, c'est que le temps d'exécution est vraiment très proche de ma simple boucle de départ!
    Je n'ai plus un temps de traitement divisé par 2 (comme je l'avais avec le tout premier exemple Linq donné par Ould Mourad) ou divisé par 15 (comme je l'avais avec ton exemple basé sur un BinarySearch).
    Il est vrai qu'aucun des deux ne me donnait par contre exactement ce que je voulais (retourner l'index du premier élément d'une liste commençant par x caractères)...et que ce n'est pas non plus une course de vitesse, comme tu me l'as fait remarqué ;-)

    J'attends donc, si tu y es disposé, de voir ce que cela pourrait donner avec l'index généré à la volée par Linq (comme tu m'en as parlé)...car la liste comporte pour l'instant 10.000 entrées mais peut encore s'agrandir...et je préfère avoir quelque chose de très rapide, bien évidemment ;-)

    Merci encore!!

    • Modifié jmdeb jeudi 1 décembre 2011 15:33
    jeudi 1 décembre 2011 15:24
  • A titre purement informatif j'arrive à qq chose comme :

    Dim q As Integer = romIndex.Select(Function(f, index) New With {.Data = f, .index = index}).
                FirstOrDefault(Function(f) f.Data.Descript.StartsWith("5000", StringComparison.CurrentCultureIgnoreCase)).Index
    

    Cela reste plus lent (mais sans différence perceptible) sans doute parce qu'en contrepartie il est nécessaire de créer des types anonymes pour numéroter les éléments. De plus il faut encore gérer le cas un peu spécial ou aucun élément n'est trouvé.

    D'après ce que je vois l'approche précédente avec Linq semble largement suffisante (même avec 1000000 d'élements cela me semble en gros instantané).

    Linq examine à priori tous les éléments car ne sais pas que le tableau est trié. Donc je dirais que le plus simple serait de ne pas même garder la contrainte de tri de ce tableau et de retourner donc la liste des éléments trouvés (et modifié un élement de cette liste modifiera effectivement l'élément présent dans le tableau). 

    Comme je disais précédemment, il est sans doute inutile de se casser la tête ou de s'ajouter des contraintes qui ne sont pas d'actualité maintenant (car elles risquent de ne l'être jamais donc on se complique la vie sans en retirer jamais aucun bénéfice).

    Au final un petit test me donne :

            ' Données de test
            Dim displayArray(1000000) As Displayed
            For i As Integer = 0 To UBound(displayArray)
                displayArray(i) = New Displayed With {.rom = i.ToString, .Descript = i.ToString}
            Next
            Dim s As New Stopwatch
            s.Start()
            ' Recherche ce qui commence par 5000
            Dim foundList As IEnumerable(Of Displayed) = displayArray.Where(Function(o) o.Descript.StartsWith("5000", StringComparison.CurrentCultureIgnoreCase))
            ' Traite les éléments trouvés
            For Each found As Displayed In foundList
                found.Descript = "Z"
                Debug.WriteLine(Array.IndexOf(displayArray, found)) ' On peut récupérer l'index si vraiment nécessaire
            Next
            s.Stop()
            ' Montre que les modifications portent bien sur les éléments stockés dans le tableau
            MessageBox.Show(displayArray(5000).Descript)
            ' Montre le temps écoulé (et pas seulement pour la recherche, le plus long est d'écrire dans la fenêtre de débogage)
            MessageBox.Show(s.ElapsedMilliseconds.ToString)
    
    


    Le tout prends environ 800 ms chez moi et 200 ms environ si je n'écris pas les index vers la fenêtre de débogage.

     


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    jeudi 1 décembre 2011 18:16
    Modérateur
  • Je voudrais tous vous remercier..et notamment Patrice qui m'a suivi de près!

    Patrice, j'ai finalement retenu une de tes premières propositions: faire un BinarySearch, pour se situer très rapidement dans l'array, puis ensuite remonter la liste jusqu'au premier élément!

    On m'a, ici et ailleurs, fait remarquer que les vitesses de traitement étaient de toutes les façons très bonnes...mais c'est plus fort que moi, j'aime me dire que mon code s'exécute très rapidement (sans doute parce que mes début à tripatouiller du basic datent du Commodore 64 ;-) ) et je me dit que je pourrais ainsi m'en resservir dans d'autres occasions où la vitesse serait critique...enfin soit, on ne se refait pas ;-)

    Alors vous allez peut-être sourire des chiffres que je vais vous donner et qui sont dans tous les cas acceptables.
    - Ma boucle de départ, testée en situation sur 5000 éléments donnait +/- 0,030 sec
    - L'utilisation de Linq donnait +/- 0,020 sec
    - L'utilisation du code ci-dessous donne +/- 0,0010 sec

    Voici donc, pour ceux que ça intéresserait, le code final:

     

    Public Shared displayArray As New List(Of Displayed)
    ' Il faudra bien sur remplir ce List (of Displayed), ce n'est pas expliqué ici ' Public Class Displayed Public Shared ReadOnly DescriptComparer As New DescriptComparerClass Public rom As String Public descript As String Public Class DescriptComparerClass 'Utilisé pour permettre une comparaison basée sur la propriété 'descript' (en l'occurence trier l'array) Implements IComparer(Of Displayed) Public Function Compare(ByVal x As Displayed, ByVal y As Displayed) As Integer Implements System.Collections.Generic.IComparer(Of Displayed).Compare Return String.Compare(x.descript, y.descript, True) End Function End Class Public Class DescriptStartsComparerClass 'Utilisé pour faire un BinarySearch sur 'descript Implements IComparer(Of Displayed) Public Function Compare(ByVal x As Displayed, ByVal y As Displayed) As Integer Implements System.Collections.Generic.IComparer(Of Displayed).Compare If x.descript.ToUpper.StartsWith(y.descript) Then Return 0 Return String.Compare(x.descript, y.descript, True) End Function End Class End Class '
    displayArray.Sort(Displayed.DescriptComparer) 'Trie l'array
    displayText = "ba" 'exemple de texte à chercher comme étant le début d'un élément descript Dim ng As New Displayed 'Crée un nouvel objet et lui attribue le texte à chercher comme propriété 'descript' ng.descript = displayText ' Cherche "en gros" un élément contenant le texte et retourne son index
    Dim ndx As Int32 = displayArray.BinarySearch(0, displayArray.Count, ng, New Displayed.DescriptStartsComparerClass) ' Et maintenant, "affinage" en remontant la liste jusqu'au 1er ékément contenant toujours ce mot<br/>If ndx >= 0 Then 'si le binary search n'a pas retourné un '-1' (rien trouvé) If ndx <= 1 Then 'à 1 on ne lance pas la procédure, on fait juste remonter l'index (évite une erreur avec un 'displayArray(-1)' dans la boucle ci-dessous) ndx = 0 Else For i = ndx - 1 To 0 Step -1 If displayArray(i).descript.ToUpper.StartsWith(displayText) Then ndx = i Else Exit For End If Next End If masterIndex = ndx 'variable contenant l'index End If


    Patrice, il y a encore quelque chose qui m'échappe un peu dans ceci:

    Public Function Compare(ByVal x As Displayed, ByVal y As Displayed) As Integer Implements System.Collections.Generic.IComparer(Of Displayed).Compare 
      If x.descript.ToUpper.StartsWith(y.descript) Then Return 0 
      Return String.Compare(x.descript, y.descript, True)
    End Function

    Ce sont les 2 'Return'! Quand je lis qu'un 0 est retourné si la condition est vérifiée, je m'attendrais a plûtot avoir la position dans l'index retournée?! Peux tu m'expliquer, stp, ce que ces return..retournent...et à qui!


    Voila, j'espère que cela pourra aider quelqu'un...et je remercie encore Patrice pour son aide cruciale!!

    • Modifié jmdeb samedi 3 décembre 2011 16:44
    • Marqué comme réponse Ciprian Duduiala lundi 5 décembre 2011 08:38
    samedi 3 décembre 2011 16:24