删除给定索引处的列表元素
本文关键字:列表元素 索引 删除 | 更新日期: 2023-09-27 18:29:11
我有一个列表,其中包含一些字符串类型的项。
List<string> lstOriginal;
我有另一个列表,其中包含应从第一个列表中删除的ID。
List<int> lstIndices;
我试着用RemoveAt()方法来完成这项工作
foreach(int indice in lstIndices)
{
lstOriginal.RemoveAt(indice);
}
但它崩溃了,并告诉我"索引超出范围"。
您需要将要返回的索引从最大到最小排序,以避免在错误的索引中删除某些内容。
foreach(int indice in lstIndices.OrderByDescending(v => v))
{
lstOriginal.RemoveAt(indice);
}
原因如下:假设有一个由五个项目组成的列表,并且您希望删除索引2
和4
处的项目。如果首先删除2
处的项目,则索引4
处的项目将位于索引3
处,索引4
将不再在列表中(导致异常)。如果向后看,所有索引都会一直存在,直到您准备删除相应的项为止。
如何填充索引列表?您可能可以使用一种更有效的RemoveAll
方法。例如,代替这个:
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
你可以这样做:
data.RemoveAll(item => SomeFunction(item));
这最大限度地减少了将项目复制到阵列中的新位置;每个项目只复制一次。
您也可以在上面的例子中使用方法组转换,而不是lambda:
data.RemoveAll(SomeFunction);
之所以会发生这种情况,是因为当您从列表中删除一个项目时,每个项目之后的索引都会有效地减少一,因此,如果您按索引递增的顺序删除它们,并且原始列表末尾附近的一些项目将被删除,那么这些索引现在是无效的,因为随着较早项目的删除,列表会变短。
最简单的解决方案是按递减顺序对索引列表进行排序(首先是最高索引),然后进行迭代。
for (int i = 0; i < indices.Count; i++)
{
items.RemoveAt(indices[i] - i);
}
My就地删除给定索引是一种方便的扩展方法。它只复制所有项目一次,因此如果要删除大量标记,它的性能会高得多。
在要移除的索引超出界限的情况下,它还会抛出ArgumentOutOfRangeException
。
public static class ListExtensions
{
public static void RemoveAllIndices<T>(this List<T> list, IEnumerable<int> indices)
{
//do not remove Distinct() call here, it's 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);
}
}
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();
lstIndices.OrderByDescending(p => p).ToList().ForEach(p => lstOriginal.RemoveAt((int)p));
附带说明一下,在foreach语句中,最好不要修改foreach运行的Ienumerable。超出范围的误差可能是这种情况的结果。