2008-11-21 95 views
37

Tengo que eliminar algunas filas de una tabla de datos. He oído que no está bien cambiar una colección mientras la repito. Entonces, en lugar de un bucle for en el que verifico si una fila cumple con las demandas de eliminación y luego marcarla como eliminada, primero debo iterar a través de la tabla de datos y agregar todas las filas en una lista, luego recorrer la lista y marcar las filas para las eliminaciones ¿Cuáles son las razones para esto y qué alternativas tengo (en lugar de utilizar la lista de filas quiero decir)?Cómo modificar o eliminar elementos de una colección enumerable mientras se itera en C#

+0

He editado el título para que sea más fácil encontrar esta pregunta. Hubo un engañador que cerré antes, pero puedo ver cómo el usuario podría haber perdido esta pregunta con el título anterior. –

Respuesta

17

Puede eliminar elementos de una colección si utiliza un simple bucle for.

Tome un vistazo a este ejemplo:

 var l = new List<int>(); 

     l.Add(0); 
     l.Add(1); 
     l.Add(2); 
     l.Add(3); 
     l.Add(4); 
     l.Add(5); 
     l.Add(6); 

     for (int i = 0; i < l.Count; i++) 
     { 
      if (l[i] % 2 == 0) 
      { 
       l.RemoveAt(i); 
       i--; 
      } 
     } 

     foreach (var i in l) 
     { 
      Console.WriteLine(i); 
     } 
+4

Esto está defectuoso ya que no todos los elementos serán revisados. Si elimina un elemento en i, entonces el elemento en i + 1 se convierte en i. Cuando i se incremente para el siguiente ciclo, omitirá el elemento que acaba de reemplazar el elemento eliminado (espero que tenga sentido). –

+1

Corregí esto. Gracias Andy por la bofetada. Pero mi punto fue que puedes cambiar una colección con un ciclo for. –

+0

¿Puedo utilizar l [i] .Delete(), ya que eliminar creará problemas con el adaptador de tabla en el procedimiento de actualización (las filas se eliminarán de la tabla en lugar de simplemente marcarse como eliminadas). – kjv

3

Un bucle while manejaría esto:

int i = 0; 
while(i < list.Count) 
{ 
    if(<codition for removing element met>) 
    { 
     list.RemoveAt(i); 
    } 
    else 
    { 
     i++; 
    } 
} 
+0

Esta solución enfrenta el mismo problema que el anterior, sus índices estarán desactivados si elimina un elemento. – Element

+2

No lo harán, ya que el índice solo se incrementa cuando el elemento no se elimina. –

2

suprimir o añadir a la lista, mientras que la iteración a través de él se puede romper, como usted ha dicho.

menudo he utilizado un enfoque de dos lista para resolver el problema:

ArrayList matches = new ArrayList(); //second list 

for MyObject obj in my_list 
{ 

    if (obj.property == value_i_care_about) 
     matches.addLast(obj); 
} 

//now modify 

for MyObject m in matches 
{ 
    my_list.remove(m); //use second list to delete from first list 
} 

//finished. 
+0

Este enfoque es realmente bueno y también seguro para subprocesos. –

+0

¿Quizás podrías editarlo para que sea el código C#? –

5

Puesto que usted está trabajando con un DataTable y la necesidad de ser capaces de persistir los cambios de vuelta al servidor con un adaptador de la tabla (véase los comentarios), aquí es un ejemplo de cómo se debe eliminar filas:

DataTable dt; 
// remove all rows where the last name starts with "B" 
foreach (DataRow row in dt.Rows) 
{ 
    if (row["LASTNAME"].ToString().StartsWith("B")) 
    { 
     // mark the row for deletion: 
     row.Delete(); 
    } 
} 

Calling borrar en las filas cambiará su propiedad RowState como eliminado, pero deje las filas eliminadas en la tabla. Si aún necesita trabajar con esta tabla antes de continuar con los cambios en el servidor (por ejemplo, si desea mostrar el contenido de la tabla menos las filas eliminadas), debe verificar el RowState de cada fila mientras lo itera de esta manera. :

foreach (DataRow row in dt.Rows) 
{ 
    if (row.RowState != DataRowState.Deleted) 
    { 
     // this row has not been deleted - go ahead and show it 
    } 
} 

filas Eliminación de la colección (como en la respuesta de Bruno) se romperá el adaptador de la tabla, y en general no debe hacerse con un DataTable.

87

Iterar al revés a través de la lista suena como un mejor enfoque, porque si quita un elemento y otros elementos "caen en la brecha", eso no importa porque ya los ha examinado. Además, no tiene que preocuparse de que su variable contraria se vuelva más grande que .Count.

 List<int> test = new List<int>(); 
     test.Add(1); 
     test.Add(2); 
     test.Add(3); 
     test.Add(4); 
     test.Add(5); 
     test.Add(6); 
     test.Add(7); 
     test.Add(8); 
     for (int i = test.Count-1; i > -1; i--) 
     { 
      if(someCondition){ 
       test.RemoveAt(i); 
      } 
     } 
0

Cuando necesito eliminar un elemento de una colección que estoy enumerando lo general enumerar a la inversa.

33

Tomando el código de @bruno, lo haría al revés.

Porque cuando retrocede, los índices de matriz faltantes no interfieren con el orden de su ciclo.

var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 }); 

for (int i = l.Count - 1; i >= 0; i--) 
    if (l[i] % 2 == 0) 
     l.RemoveAt(i); 

foreach (var i in l) 
{ 
    Console.WriteLine(i); 
} 

Pero seriuosly, en estos días, que haría uso de LINQ: solución

var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 }); 

l.RemoveAll(n => n % 2 == 0); 
+8

+1 para darse cuenta del poder de LINQ para algo como esto. ¿Por qué hacer las cosas a lo largo cuando no tienes que – BenAlabaster

+0

por qué no definir la lista l como List en lugar de var si sabes que va a ser una lista ? +1 a – sebagomez

+6

+ 1 para RemoveAll. Nota RemoveAll no es estrictamente LINQ. Está disponible para cualquier clase 'List' en .NET 2.0 sin soporte de LINQ. –

3

de chakrit también se puede utilizar si se apuntan a .NET 2.0 (sin expresiones LINQ/lambda) utilizando un delegado en lugar de una expresión lambda:

public bool IsMatch(int item) { 
    return (item % 3 == 1); // put whatever condition you want here 
} 
public void RemoveMatching() { 
    List<int> x = new List<int>(); 
    x.RemoveAll(new Predicate<int>(IsMatch)); 
} 
Cuestiones relacionadas