2008-11-21 3 views
37

Мне нужно удалить некоторые строки из таблицы данных. Я слышал, что менять коллекцию пока не удается. Поэтому вместо цикла for, в котором я проверяю, удовлетворяет ли строка требованиям к удалению, а затем помечает его как удаленный, я должен сначала перебрать таблицу данных и добавить все строки в список, затем выполнить итерацию по списку и пометить строки для удаления. Каковы причины этого и какие у меня альтернативы (вместо того, чтобы использовать список строк, который я имею в виду) ?.Как изменить или удалить элементы из перечислимой коллекции, итерации через нее в C#

+0

Я отредактировал заголовок, чтобы было легче найти этот вопрос. Был обман, который я закрыл раньше, но я вижу, как пользователь мог пропустить этот вопрос со старым названием. –

ответ

17

Вы можете удалить элементы из коллекции, если используете простой цикл for.

Взгляните на этот пример:

 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

Это неверно, так как не все элементы будут проверены. Если вы удалите элемент в i, тогда элемент в i + 1 станет i. Когда i затем увеличивается на следующий цикл, он пропускает элемент, который только что заменил удаленный элемент (надеюсь, что это имеет смысл). –

+1

Я исправил это. Спасибо Энди за пощечину. Но я хотел сказать, что вы можете изменить коллекцию с циклом for. –

+0

Могу ли я использовать l [i] .Delete(), так как removeat создаст проблемы с адаптером таблицы в процедуре обновления (строки будут удалены из таблицы вместо того, чтобы просто быть помечены как удаленные). – kjv

3

Через некоторое время цикл будет справиться с этим:

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

Это решение сталкивается с той же проблемой, что и выше, ваши индексы будут отключены, если вы удалите элемент. – Element

+2

Нет, они не будут, поскольку индекс только увеличивается, когда элемент не удаляется. –

2

Удаление или добавление в список в то время как переборе он может разорвать его, как вы сказали.

Я часто использовал два списка подход к решению проблемы:

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

Этот подход на самом деле хорош и потокобезопасен. –

+0

Возможно, вы можете отредактировать его как код C#? –

5

Поскольку вы работаете с DataTable и должны быть в состоянии сохраняться какие-либо изменения на сервер с адаптером таблицы (см комментарии), вот пример того, как следует удалить строки:

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(); 
    } 
} 

Вызов удаления по строкам изменит их RowState свойство удалён, но оставить удаленные строки в таблице. Если вам все еще нужно работать с этой таблицей, прежде чем сохранять изменения на сервере (например, если вы хотите отобразить содержимое таблицы за вычетом удаленных строк), вам нужно проверить RowState каждой строки, как вы итерации через нее :

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

Удаление строк из коллекции (как в ответ Брюно) сломается адаптер таблицы, и как правило, не будет сделано с DataTable.

87

Итерация назад через список звучит как лучший подход, потому что, если вы удалите элемент и другие элементы «попадаете в промежуток», это не имеет значения, потому что вы уже посмотрели на них. Кроме того, вам не нужно беспокоиться о том, что ваша переменная счетчика становится больше, чем значение .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

Когда мне нужно удалить элемент из коллекции, я перечисляющем я обычно перечислить его в обратном направлении.

33

Принимая код @bruno, я бы сделал это назад.

Потому что, когда вы двигаетесь назад, недостающие индексы массива не мешают порядку вашего цикла.

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); 
} 

Но seriuosly, в эти дни, я хотел бы использовать LINQ: решение

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

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

+1 для реализации мощности LINQ для чего-то подобного. Почему дела идут так долго, когда вам не нужно – BenAlabaster

+0

, почему бы не определить список l как Список вместо var, если вы знаете, что это будет список ? +1 хотя – sebagomez

+6

+ 1 для RemoveAll. Примечание. RemoveAll не является строго LINQ. Он доступен для любого класса List в .NET 2.0 без поддержки LINQ. –

3

chakrit также может быть использована, если вы ориентируетесь .NET 2.0 (без LINQ/лямбда-выражений), используя делегат, а не лямбда-выражение:

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)); 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^