List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; List<int> toRemove = new List<int>() { 2, 4, 6, 8 }; foreach(int item in list) { if(toRemove.Contains(item)) { list.Remove(item); } }
Of course when this code runs, you immediately get the "Collection was modified; enumeration may not execute." error. Easily fixed by looping over a copy of the original list:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; List<int> toRemove = new List<int>() { 2, 4, 6, 8 }; List<int> listCopy = new List<int>(list); foreach(int item in listCopy) { if(toRemove.Contains(item)) { list.Remove(item); } }
So, I created my own "loop-safe" list class, called LoopList
/// <summary> /// List that allows modification inside a foreach loop. /// </summary> /// <typeparam name="T">The type of object contained in the list.</typeparam> public class LoopList<T> : IList<T> { /// <summary> /// The items in the LoopList. /// </summary> List<T> _items; /// <summary> /// Default LoopList constructor, takes an optional isReadOnly flag. /// </summary> /// <param name="isReadOnly">Whether this will be a read-only collection.</param> public LoopList(bool isReadOnly = false) { _items = new List<T>(); IsReadOnly = isReadOnly; } /// <summary> /// Constructor that takes an input collection to copy, also takes an optional isReadOnly flag. /// </summary> /// <param name="collection">The input collection to copy.</param> /// <param name="isReadOnly">Whether this will be a read-only collection.</param> public LoopList(IEnumerable<T> collection, bool isReadOnly = false) { _items = new List<T>(collection); IsReadOnly = isReadOnly; } /// <summary> /// Constructor that takes an initial capacity to allocate, also takes an optional isReadOnly flag. /// </summary> /// <param name="capacity">The initial capacity to allocate.</param> /// <param name="isReadOnly">Whether this will be a read-only collection.</param> public LoopList(int capacity, bool isReadOnly = false) { _items = new List<T>(capacity); IsReadOnly = isReadOnly; } /// <summary> /// Get the Index of an item in the LoopList. /// </summary> /// <param name="item">The item to find the index of.</param> /// <<returns>The index of the specified item.</returns> public int IndexOf(T item) { return _items.IndexOf(item); } /// <summary> /// Insert an item at the specified index. /// </summary> /// <param name="index">The index at which to insert the item.</param> /// <param name="item">The item to insert.</param> public void Insert(int index, T item) { if (IsReadOnly) { throw new InvalidOperationException("Attempt to Insert item into ReadOnly LoopList"); } _items.Insert(index, item); } /// <summary> /// Remove the item at the specified index. /// </summary> /// <param name="index">The index of the item to remove.</param> public void RemoveAt(int index) { if (IsReadOnly) { throw new InvalidOperationException("Attempt to Remove item from ReadOnly LoopList"); } _items.RemoveAt(index); } /// <summary> /// Get or set the item at a specified index. /// </summary> /// <param name="index">The index of the item to get or set.</param> /// <returns>The item at the specified index.</returns> public T this[int index] { get { return _items[index]; } set { if (IsReadOnly) { throw new InvalidOperationException("Attempt to set item value in ReadOnly LoopList"); } _items[index] = value; } } /// <summary> /// Add an item to the LoopList. /// </summary> /// <param name="item">The item to add.</param> public void Add(T item) { if (IsReadOnly) { throw new InvalidOperationException("Attempt to Add item to ReadOnly LoopList"); } _items.Add(item); } /// <summary> /// Remove all items from the LoopList. /// </summary> public void Clear() { if (IsReadOnly) { throw new InvalidOperationException("Attempt to Clear ReadOnly LoopList"); } _items.Clear(); } /// <summary> /// Check if the LoopList contains an item. /// </summary> /// <param name="item">The item to look for.</param> /// <<returns>True if the LoopList contains the item, false otherwise.</returns> public bool Contains(T item) { return _items.Contains(item); } /// <summary> /// Copy the items in the LoopList to an array. /// </summary> /// <param name="array">The array to copy items into.</param> /// <param name="arrayIndex">The index in the array to start copying items to.</param> public void CopyTo(T[] array, int arrayIndex) { _items.CopyTo(array, arrayIndex); } /// <summary> /// The number of items in the LoopList. /// </summary> public int Count { get { return _items.Count; } } /// <summary> /// A flag indicating whether the LoopList is read-only. /// </summary> public bool IsReadOnly { get; private set; } /// <summary> /// Remove the specified item from the LoopList. /// </summary> /// <param name="item">The item to remove.</param> /// <returns>True if the item was successfully removed, false otherwise.</returns> public bool Remove(T item) { if (IsReadOnly) { throw new InvalidOperationException("Attempt to Remove item from ReadOnly LoopList"); } return _items.Remove(item); } /// <summary> /// Get an enumerator to loop over the items in the LoopList. /// </summary> /// <returns>An Enumerator over items of the type contained in the LoopList.</returns> public IEnumerator<T> GetEnumerator() { List<T> tempItems = new List<T>(_items); return tempItems.GetEnumerator(); } /// <summary> /// Get an enumerator to loop over the items in the LoopList. /// </summary> /// <returns>An Enumerator over items of the type contained in the LoopList.</returns> System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }
And, voila! It works:
LoopList<int> list = new LoopList<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; List<int> toRemove = new List<int>() { 2, 4, 6, 8 }; foreach (int item in list) { if (toRemove.Contains(item)) { list.Remove(item); } }