Delete list items at given indices

I have a list that contains some elements of a type string.

List<string> lstOriginal; 

I have another list that contains identifiers that should be removed from the first list.

 List<int> lstIndices; 

I tried to do this work using the RemoveAt () method,

 foreach(int indice in lstIndices) { lstOriginal.RemoveAt(indice); } 

but it crashes and told me that the index is "out of range."

+6
source share
7 answers

You need to sort the indexes that you would like to return from largest to smallest in order to avoid deleting anything with the wrong index.

 foreach(int indice in lstIndices.OrderByDescending(v => v)) { lstOriginal.RemoveAt(indice); } 

Here's why: let there be a list of five items, and you want to remove items at indices 2 and 4 . If you first delete item 2 , the item that was in index 4 will have index 3 , and index 4 will no longer be on the list (raising its exception). If you go back, all indexes will be available until you are ready to remove the corresponding item.

+26
source

The reason this happens is that when you remove an item from the list, the index of each item after it effectively decreases by one, so if you delete them with an increase in the index and some items at the end of the original list should have been deleted. these indexes are no longer valid because the list becomes shorter as the earlier elements are removed.

The simplest solution is to sort the list of indexes in descending order (first the highest index), and then iterate over it.

+5
source

How do you populate the index list? There is a more effective RemoveAll method that you could use. For example, instead:

 var indices = new List<int>(); int index = 0; foreach (var item in data) if (SomeFunction(data)) indices.Add(index++); //then some logic to remove the items 

You can do it:

 data.RemoveAll(item => SomeFunction(item)); 

This minimizes copying elements to new positions in the array; each item is copied only once.

You can also use method group conversion in the above example instead of lambda:

 data.RemoveAll(SomeFunction); 
+5
source
 for (int i = 0; i < indices.Count; i++) { items.RemoveAt(indices[i] - i); } 
+4
source
  var array = lstOriginal.ConvertAll(item => new int?(item)).ToArray(); lstIndices.ForEach(index => array[index] = null); lstOriginal = array.Where(item => item.HasValue).Select(item => item.Value).ToList(); 
+1
source

My removal in place of given indices as a convenient extension method. It copies all elements only once, so it is much more efficient if you need to delete a large number of directions.

It also throws an ArgumentOutOfRangeException if the index to delete is out of scope.

  public static class ListExtensions { public static void RemoveAllIndices<T>(this List<T> list, IEnumerable<int> indices) { //do not remove Distinct() call here, it important var indicesOrdered = indices.Distinct().ToArray(); if(indicesOrdered.Length == 0) return; Array.Sort(indicesOrdered); if (indicesOrdered[0] < 0 || indicesOrdered[indicesOrdered.Length - 1] >= list.Count) throw new ArgumentOutOfRangeException(); int indexToRemove = 0; int newIdx = 0; for (int originalIdx = 0; originalIdx < list.Count; originalIdx++) { if(indexToRemove < indicesOrdered.Length && indicesOrdered[indexToRemove] == originalIdx) { indexToRemove++; } else { list[newIdx++] = list[originalIdx]; } } list.RemoveRange(newIdx, list.Count - newIdx); } } 
0
source
  lstIndices.OrderByDescending(p => p).ToList().ForEach(p => lstOriginal.RemoveAt((int)p)); 

As a side note in foreach operations, it’s best not to change the Ienumerable on which foreach is running. This situation is likely to cause an out of range error.

-2
source

Source: https://habr.com/ru/post/911862/


All Articles