webdevqa.jp.net

.NET-「foreach」ループ内のList <T>から削除

私はwantというコードを持っています:

List<Type> Os;

...

foreach (Type o in Os)
    if (o.cond)
        return;  // Quitting early is important for my case!
    else
        Os.Remove(o);

... // Other code

リスト上のforeachループ内にいる場合はリストから削除できないため、これは機能しません。

問題を解決する一般的な方法はありますか?

必要に応じて、別のタイプに切り替えることができます。

オプション2:

List<Type> Os;

...

while (Os.Count != 0)
     if (Os[0].cond)
         return;
     else
         Os.RemoveAt(0);

... // Other code

Glyいですが、動作するはずです。

46
BCS

foreachループ内でこれを本当に行う必要がありますか?

これにより、例と同じ結果が得られます。つまり、条件に一致する最初のアイテムまでリストからすべてのアイテムが削除されます(条件に一致するものがない場合はすべてのアイテムが削除されます)。

int index = Os.FindIndex(x => x.cond);

if (index > 0)
    Os.RemoveRange(0, index);
else if (index == -1)
    Os.Clear();
30
LukeH

リストを逆方向に反復できます。

for (int i = myList.Count - 1; i >= 0; i--)
{
    if (whatever) myList.RemoveAt(i);
}

削除していないアイテムを見つけたときに終了したいというあなたのコメントに応えて、whileループを使用することが最善の解決策になります。

58
Jon B

Foreachループ内で繰り返し処理しているコレクションから何も削除しないでください。基本的には、あなたが座っているブランチを見たようなものです。

While代替を使用します。行く方法です。

55
User

私はJavaプログラマですが、このようなものは機能します:

List<Type> Os;
List<Type> Temp;
...
foreach (Type o in Os)
    if (o.cond)
        Temp.add(o);
Os.removeAll(Temp);  
15
Milhous

分析ライブラリに問題がありました。私はこれを試しました:

for (int i = 0; i < list.Count; i++)
{                
   if (/*condition*/)
   {
       list.RemoveAt(i);
       i--;
   }
}

とても簡単ですが、ブレークポイントについては考えていません。

13
Anzurio

[〜#〜] why [〜#〜]で最も簡単なEASIEST SOLUTIONを次に示します。

問題:

通常、元のリストから削除するため、リストカウントとイテレータの場所を維持する問題が発生します。

_List<Type> Os = ....;
Os.ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);
_

解決策-_LINQ.ForEach_:

追加したのはToList()のみでした。これにより、ForEachを実行する新しいリストが作成されるため、元のリストを削除しながら、リスト全体を繰り返し処理できます。

_List<Type> Os = ....;
Os.ToList().ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);
_

解決策-通常のforeach

この手法は、通常のforeachステートメントでも機能します。

_List<Type> Os = ....;
foreach(Type o in Os.ToList()) {
  if(!o.cond) Os.Remove(o);
}
_

元のリストにstruct要素が含まれている場合、このソリューションは機能しないことに注意してください。

12
sonjz

あなたが何か他のものを要求したことは知っていますが、要素の束を条件付きで削除したい場合は、ラムダ式を使用できます:

Os.RemoveAll(o => !o.cond);
11
Day_Dreamer
 Os.RemoveAll(delegate(int x) { return /// });
9
dirkgently

述語を満たさない最初のアイテムのインデックスを見つけて、RemoveRange(0、index)を実行してみます。他に何もなければ、Remove呼び出しは少なくなるはずです。

4
bh213

更新:完全を期すために追加

いくつかの回答があったように、GetEnumerator()(例foreach)を使用してコレクションを反復している間は、コレクションを変更しないでください。フレームワークは、例外をスローすることでこれを行うことを防ぎます。これに対する一般的な解決策は、forで「手動で」反復することです(他の回答を参照)。インデックスに注意して、アイテムをスキップしたり、同じアイテムを2回再評価したりしないようにしてください(i--または逆方向に繰り返します)。

ただし、特定のケースでは、削除操作を最適化することができます...以下の元の回答。


特定の条件が満たされるまですべてのアイテムを削除する場合(コードが実行することです)、これを行うことができます。

bool exitCondition;

while(list.Count > 0 && !(exitCondition = list[0].Condition))
   list.RemoveAt(0);

または、単一の削除操作を使用する場合:

SomeType exitCondition;
int index = list.FindIndex(i => i.Condition);

if(index < 0)
    list.Clear();
else
{
    exitCondition = list[0].State;
    list.RemoveRange(0, count);
}

注:item.Conditionboolで、私はitem.State終了条件を保存します。

更新:両方の例に境界チェックと保存終了条件を追加

3
Lucas

あなたはlinqでそれを行うことができます

MyList = MyList.Where(x=>(someCondition(x)==true)).ToList()
2
mLar

Anzurioのソリューションはおそらく最も簡単ですが、ユーティリティライブラリに多数のインターフェイス/クラスを追加してもかまわない場合は、もう1つのクリーンなソリューションがあります。

このように書くことができます

List<Type> Os;
...
var en = Os.GetRemovableEnumerator();
while (en.MoveNext())
{
    if (en.Current.Cond)
        en.Remove();
}

JavaのIterator<T>.remove 、ユーティリティライブラリに:

static class Extensions
{
    public static IRemovableEnumerator<T> GetRemovableEnumerator<T>(this IList<T> l)
    {
        return new ListRemovableEnumerator<T>(l);
    }
}

interface IRemovableEnumerator<T> : IEnumerator<T>
{
    void Remove();
}

class ListRemovableEnumerator<T> : IRemovableEnumerator<T>
{
    private readonly IList<T> _list;
    private int _count;
    private int _index;
    public ListRemovableEnumerator(IList<T> list)
    {
        _list = list;
        _count = list.Count;
        _index = -1;
    }

    private void ThrowOnModification()
    {
        if (_list.Count != _count)
            throw new InvalidOperationException("List was modified after creation of enumerator");
    }
    public void Dispose()
    {
    }

    public bool MoveNext()
    {
        ThrowOnModification();
        if (_index + 1 == _count)
            return false;
        _index++;
        return true;
    }

    public void Reset()
    {
        ThrowOnModification();
        _index = -1;
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public T Current
    {
        get { return _list[_index]; }
    }

    public void Remove()
    {
        ThrowOnModification();
        _list.RemoveAt(_index);
        _index--;
        _count--;
    }
}
1

これについては、リスト内のアイテムを繰り返し処理しながら削除するで詳しく説明しています。

彼らは提案する:

for(int i = 0; i < count; i++)
{
    int elementToRemove = list.Find(<Predicate to find the element>);

    list.Remove(elementToRemove);
}
1
Ethan Heilman

リストがあまり大きくないことがわかっている場合は、使用できます

foreach (Type o in new List<Type>(Os))
    ....

これにより、リストの一時的な複製が作成されます。これにより、remove()呼び出しはイテレータと干渉しなくなります。

1
Nik

Enumerable.SkipWhile()を見てください

Enumerable.SkipWhile( x => condition).ToList()

通常、リストを変更しないと、ライブがずっと簡単になります。 :)

1
leppie

リストから削除するアイテムを追加し、RemoveAllを使用してこれらのアイテムを削除します。

List<Type> Os;
List<Type> OsToRemove=new List<Type>();
...
foreach (Type o in Os){
    if (o.cond)
        return;
    else
        OsToRemove.Add(o);
}
Os.RemoveAll(o => OsToRemove.Contains(o));
0
Wei Jian

私は同じ問題を抱えていて、次を使用して解決しました:

foreach (Type o in (new List(Os))) { if (something) Os.Remove(o); }

リストのコピーを反復処理し、元のリストから削除します。

0
Christiaan Maks