web-dev-qa-db-ja.com

C#リスト-ループ/反復中のアイテムの削除

次のコードスニペットがあるとします。

var data=new List<string>(){"One","Two","Three"};
for(int i=0 ; i<data.Count ; i++){
  if(data[i]=="One"){
    data.RemoveAt(i);
  }
}

次のコードは例外をスローします。

私の質問は、この例外を回避し、ループ中に要素を削除する最良の方法は何ですか?

43

要素を削除する必要がある場合は、リストの最後から要素を削除できるように、逆方向に反復する必要があります。

var data=new List<string>(){"One","Two","Three"};
for(int i=data.Count - 1; i > -1; i--)
{
    if(data[i]=="One")
    {
        data.RemoveAt(i);
    }
}

ただし、LINQを使用してこれを行うより効率的な方法があります(他の回答で示されているように)。

93
ChrisF

List<T>.RemoveAll これを処理するには:

data.RemoveAll(elem => elem == "One");
43
Reed Copsey

私はたまたまforeach.ToArray()を使用してこのための簡単なソリューションに出くわしました

  var data=new List<string>(){"One","Two","Three"};
   foreach ( var d in data.ToArray()){
      if(d =="One"){
        data.Remove(d);
      }
    }
10
Pushpendra

次のような前方移動ループを使用することもできます。

_var data = new List<string>() { "One", "Two", "Three", "One", "One", "Four" };
for (int i = 0; i < data.Count; i++)
{
    if (data[i] == "One")
    {
        data.RemoveAt(i--);
    }
}
_

この行data.RemoveAt(i--);は、リストから項目が削除された場合に、ループの終わりで反復変数の増分の効果を停止します。

現在の反復値でインデックスからアイテムを削除し、アイテムを削除した後、イテレータは現在の値よりも1つ少ない値に設定されます。ループの終わりに、ループ本体の増分はそれを次の有効なインデックスに移動します。

ここに働く dotfiddle

(IMO、これらは理解しやすいので、このような状況では個人的に逆ループを使用していることに注意してください、ここでのこの答えは、それを達成する別の方法を表示するためのものです)

4
Habib

ChrisFの逆反復法を試して、アイテムを削除できます。

また、単に次のことができます。

List.Remove("One");

または:

List.RemoveAll(i => i == "One"); // removes all instances

そして、それで終わりです。コレクションを繰り返して単一のアイテムを削除しても意味がありません。

4
Justin Niessner

イテレータ変数を単純にデクリメントしてみませんか?

var data=new List<string>(){"One","Two","Three"};
for(int i=0 ; i<data.Count ; i++){
  if(data[i]=="One"){
    data.RemoveAt(i);
    i--; // <<<<<<<<<<<
  }
}
2
Pewpew

ここに汚いトリックがあり、その批評はどうなるのだろうか?

var data=new List<string>(){"One","Two","Three"};
foreach (string itm in (data.ToArray()))
{
  if string.Compare(name, "one", true) == 0) data.Remove(name);
}
1
B H

以下の一般的な解決策は、リストのコピーを作成し、負のインデックスを処理します。

foreach (void item_loopVariable in MyList.ToList) {
    item = item_loopVariable;

}
1
alwaysVBNET
        var data = new List<string>() { "One", "Two", "Three" };
        data.RemoveAll(p=>p=="One");
0
Nivid Dholakia
var data=new List<string>(){"One","Two","Three"};
for(int i=0; i<data.Count; ){
  if(data[i]=="One") data.RemoveAt(i);
  else ++i;
}
0
Tutankhamen

Stackクラスを使用できます

        Stack<string> myStack = new Stack<string>();

        foreach (var item in Enumerable.Range(1,1001))
            myStack.Push("Str " + item.ToString());

        while (myStack.Any())
            Console.WriteLine("Now {0} items in Stack, removed item is {1}",myStack.Count,myStack.Pop());

        Console.ReadKey();
0
Biju kalanjoor

リストから複数のアイテムを削除する必要がありました。そのため、リストカウントを再初期化しました。他のより良いオプションはありますか?

for (int i = dtList.Count - 1; dtList.Count > 0; )
{
     DateTime tempDate = dtList[i].Item1.Date;
     var selectDates = dtList.FindAll(x => x.Item1.Date == tempDate.Date);
     selectDates.Sort((a, b) => a.Item1.CompareTo(b.Item1));
     dtFilteredList.Add(Tuple.Create(selectDates[0].Item1, selectDates[0].Item2));
     dtList.RemoveAll(x => x.Item1.Date == tempDate.Date);
     i = dtList.Count - 1;
}
0
Madhu