LINQ:RemoveAll 并删除元素
Posted
技术标签:
【中文标题】LINQ:RemoveAll 并删除元素【英文标题】:LINQ: RemoveAll and get elements removed 【发布时间】:2012-04-28 00:58:01 【问题描述】:这是从列表中删除符合某些条件的项目然后获取这些项目的最简单方法。
我可以从几个方面考虑,我不知道哪个是最好的:
var subList = list.Where(x => x.Condition);
list.RemoveAll(x => x.Condition);
或
var subList = list.Where(x => x.Condition);
list.RemoveAll(x => subList.Contains(x));
这是最好的方法之一吗?如果是,是哪一个?如果不是,我该怎么办?
【问题讨论】:
【参考方案1】:第一个选项很好,但它会导致两次收集。您可以通过执行谓词中的附加逻辑一次性完成:
var removedItems = new List<Example>();
list.RemoveAll(x =>
if (x.Condition)
removedItems.Add(x);
return true;
return false;
);
您也可以将其包装到扩展中以方便使用:
public static class ListExtensions
public static int RemoveAll<T>(this List<T> list, Predicate<T> predicate, Action<T> action)
return list.RemoveAll(item =>
if (predicate(item))
action(item);
return true;
return false;
);
并像这样使用:
var removedItems = new List<Example>();
list.RemoveAll(x => x.Condition, x => removedItems.Add(x));
【讨论】:
这确实是最好的答案。它最大限度地减少了对集合的运行并利用了内置函数。所有其他答案最终都会对列表中的每个元素调用 match 函数两次,生成多个列表,或者使用更重的结构,如查找。【参考方案2】:我也在寻找同样的东西。我想对要删除的项目进行一些错误记录。由于我添加了一大堆验证规则,remove-and-log 调用应该尽可能简洁。
我做了以下扩展方法:
public static class ListExtensions
/// <summary>
/// Modifies the list by removing all items that match the predicate. Outputs the removed items.
/// </summary>
public static void RemoveWhere<T>(this List<T> input, Predicate<T> predicate, out List<T> removedItems)
removedItems = input.Where(item => predicate(item)).ToList();
input.RemoveAll(predicate);
/// <summary>
/// Modifies the list by removing all items that match the predicate. Calls the given action for each removed item.
/// </summary>
public static void RemoveWhere<T>(this List<T> input, Predicate<T> predicate, Action<T> actionOnRemovedItem)
RemoveWhere(input, predicate, out var removedItems);
foreach (var removedItem in removedItems) actionOnRemovedItem(removedItem);
示例用法:
items.RemoveWhere(item => item.IsWrong, removedItem =>
errorLog.AppendLine($"removedItem was wrong."));
【讨论】:
【参考方案3】:我喜欢使用函数式编程方法(只做新的东西,不要修改现有的东西)。 ToLookup
的一个优点是您可以处理多个项目的双向拆分。
ILookup<bool, Customer> lookup = list.ToLookup(x => x.Condition);
List<Customer> sublist = lookup[true].ToList();
list = lookup[false].ToList();
或者如果你需要修改原始实例...
list.Clear();
list.AddRange(lookup[false]);
【讨论】:
我认为它非常复杂(几乎没有知识,我认为它不是真正的性能)。这有什么好处吗? 条件仅对每个项目进行一次评估。列表实例不会被修改,如果该列表实例在线程之间共享,这将是一个很大的优势。 这真是个天才的想法。【参考方案4】:出于可读性目的,我会选择第一个选项,并注意您应该首先实现列表,否则您将丢失下一行尝试选择的项目:
var sublist = list.Where(x => x.Condition).ToArray();
list.RemoveAll(x => x.Condition);
第二个例子是 O(n^2) 无缘无故,最后一个很好,但可读性较差。
编辑:现在我重新阅读了您的最后一个示例,请注意,现在写的内容将删除所有其他项目。您缺少条件检查,删除行实际上应该是 list.RemoveAt(i--);
,因为 i+1
th 元素在删除后变为 i
th 元素,并且当您增加 i
时,您将跳过它。
【讨论】:
它实际上是 O(n^3),但我假设缺乏物化只是你的想法;) 是否会使用第二条指令从 subList 中删除项目(如我所写)? :O 你永远不会从sublist
中删除,如果我没看错的话你也不打算这样做。
你的编辑是对的,我会删除第三个选项,因为它真的错了,如果把它改正会很不可读
当你在一个集合上运行一个 linq 查询时,你不会得到一个数组,你会得到一个对象,当你迭代它时你运行你的实际选择.所以举你的第一个例子。 subList
将只是一个对象,您从主数组中删除项目,然后当您执行 foreach(var item in subList)
时,您什么也得不到,因为条件总是返回 false。以上是关于LINQ:RemoveAll 并删除元素的主要内容,如果未能解决你的问题,请参考以下文章