Pregunta

Estoy escribiendo una aplicación para comparar cada elemento en ListBox1 con todos los elementos en ListBox2. Si se encuentra el elemento, elimínelo de ambas listas. El objetivo es solo que los elementos que no se encontraron permanecen en ambas listas.

El problema es que la aplicación simplemente cuelga y nunca obtengo ningún resultado. Miré mi código varias veces y no puedo entender qué está pasando (programar novato, sé ...).

¿Puede alguien ayudarme con esto?

Fragmento de código:

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

    Dim a As String
    Dim b As String
    Dim y As String

    For i As Integer = 0 To ListBox1.Items.Count - 1
        a = ListBox1.Items(i)
        y = 1
        Do While y = 1
            For x As Integer = 0 To ListBox2.Items.Count - 1
                b = ListBox2.Items(x)
                Dim res As Int16 = String.Compare(a, b)
                If res = 0 Then
                    y = 0
                    ListBox2.Items.Remove(i)
                    ListBox2.Items.Remove(x)
                ElseIf x = ListBox1.Items.Count Then
                    Exit Do
                End If
            Next
        Loop
    Next
End Sub
¿Fue útil?

Solución

Si listbox1.items.count es más que listbox2.Items.count - 1, x nunca igualará listbox1.items.count, por lo que la salida do nunca se ejecutará, y el código simplemente se inclinará sin parar en el

Do While y = 1

¿Ha considerado usar LINQ, por ejemplo, para una gestión de listas más fácil?

Editar: Además, está mal eliminar un elemento de la lista que está atravesando con un para (es francamente ilegal hacerlo con un para cada uno) porque cada eliminación compensará el contador de bucle.

Edit2: aquí hay un fragmento de Linq que logra la tarea:

    Dim itemsFirst = (From item As String In ListBox1.Items Select item)
    Dim itemsSecond = (From item As String In ListBox2.Items Select item)

    Dim dupes = System.Linq.Enumerable.Intersect(itemsFirst, itemsSecond).ToList
    For Each item In dupes
        ListBox1.Items.Remove(item)
        ListBox2.Items.Remove(item)
    Next item

Lo que hace es básicamente extraer las cadenas de ambas listas (esto es necesario porque la colección ListBox.Items es un poco extraña)

Después de eso, ejecutamos el método Intersect y copiamos los resultados en una lista. (la parte .tolista) La copia es una parte requerida porque, de lo contrario, los dupes solo serían un subconjunto de los elementos del cuadro de lista, y una vez más trataríamos de levantarnos a nosotros mismos tirando de nuestras zapatillas.

La última parte es solo un simple bucle de eliminación, que elimina los elementos de la colección

Otros consejos

tú tienes

ElseIf x = ListBox1.Items.Count Then
    Exit Do

Cuando debería ser

ElseIf x = ListBox1.Items.Count - 1 Then
    Exit Do

Porque su bucle for cambiará x para contar, y luego saldrá sin iterar en ese valor.

No solo eso, sino por qué hay un Do bucle de todos modos? No hay necesidad de seguir iterando el mismo cuadro de lista interno en busca de duplicados, ¿verdad?

Y en tercer lugar, no debes eliminar las cosas mientras iteras a través de ellas. En su caso, los bucles para el recuento reutilizando, por lo que es "seguro", pero la operación de eliminación reindexará las cosas, por lo que debe restar 1 de sus iteradores I y X cuando se quite, de modo que la próxima no se saltea la reintegración .

Pensando en el segundo lugar, tal vez pones eso Do Luce allí para cubrir los elementos se saltó el tiempo anterior, como se mencionó en mi tercer punto.

Si está usando Visual Studio 2008 o posterior:

Dim dupes = Listbox1.Items.Cast(Of String)().Intersect(Listbox2.Items.Cast(Of String)()).ToArray() 
For Each item As String in dupes
    Listbox1.Items.Remove(item)
    Listbox2.Items.Remove(item)
Next item

Ejecuté una prueba de tres métodos diferentes. Son Joels, Sweko y los míos. Estaba haciendo esto para probar el rendimiento, pero descubrí que los resultados no son los mismos, Los listboxes no son los mismos. Aquí está el código que solía probar, para que pueda ser el juez. Probablemente un error tonto de mi parte.

Dim stpw As New Stopwatch

Private Sub Button1_Click(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) Handles Button1.Click
    Debug.WriteLine("")
    loadLBTD() ''#load test data

    doSTPW("Test 1 Start", False) ''#mine
    ''#get rid of dupes <<<<<<<<<<<<<<
    Dim dupeL As New List(Of String)
    For x As Integer = ListBox1.Items.Count - 1 To 0 Step -1
        If ListBox2.Items.Contains(ListBox1.Items(x)) Then
            dupeL.Add(ListBox1.Items(x))
            ListBox1.Items.RemoveAt(x)
        End If
    Next
    For Each s As String In dupeL
        ListBox2.Items.Remove(s)
    Next
    doSTPW("Test 1 End")

    loadLBTD() ''#load test data

    doSTPW("Test 2 Start", False) ''#sweko
    ''#get rid of dupes <<<<<<<<<<<<<<
    ''#I had to set Option Strict to Off to get this to work <<<<<<<
    Dim itemsFirst = (From item As String In ListBox1.Items Select item)
    Dim itemsSecond = (From item As String In ListBox2.Items Select item)

    Dim dupes = System.Linq.Enumerable.Intersect(itemsFirst, itemsSecond).ToList
    For Each item In dupes
        ListBox1.Items.Remove(item)
        ListBox2.Items.Remove(item)
    Next item
    doSTPW("Test 2 End")

    loadLBTD() ''#load test data

    doSTPW("Test 3 Start", False) ''#joel
    ''#get rid of dupes <<<<<<<<<<<<<<
    Dim dupes2 = ListBox1.Items.Cast(Of String)().Intersect(ListBox2.Items.Cast(Of String)()).ToArray()
    For Each item As String In dupes2
        ListBox1.Items.Remove(item)
        ListBox2.Items.Remove(item)
    Next item
    doSTPW("Test 3 End")
End Sub

Private Sub doSTPW(ByVal someText As String, Optional ByVal showTM As Boolean = True)
    stpw.Stop() ''#stop the clock
    If flip Then Debug.Write("'T ") Else Debug.Write("'F ")
    Debug.Write("LBCnts " & ListBox1.Items.Count & " " & ListBox2.Items.Count)
    Dim s As String
    If showTM Then
        s = String.Format("  {0}  {1}", someText, stpw.ElapsedTicks.ToString("N0"))
    Else
        s = String.Format("  {0}", someText)
    End If
    Debug.WriteLine(s)
    stpw.Reset() ''#reset and start clock
    stpw.Start()
End Sub

Dim flip As Boolean = False
Private Sub loadLBTD()
    ''#Create test data 
    Dim tl1() As String = New String() {"A", "X", "y", "z", "B", "w", "X", "y", "z"}
    Dim tl2() As String = New String() {"A", "y", "z", "Q", "A", "y", "z", "Q", "A", "y", "z", "Q"}
    ListBox1.Items.Clear()
    ListBox2.Items.Clear()
    ''#load listboxes
    If flip Then
        ListBox1.Items.AddRange(tl2)
        ListBox2.Items.AddRange(tl1)
    Else
        ListBox1.Items.AddRange(tl1)
        ListBox2.Items.AddRange(tl2)
    End If
    ''#end of test data setup
End Sub

Además, como se esperaba, Linq es más conciso pero más lento. Si el código se usa con poca frecuencia, no importa. Tuve una mala experiencia con Linq y un tamiz de Eratosthenes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top