【问题标题】:LINQ: RemoveAll and get elements removedLINQ:RemoveAll 并删除元素
【发布时间】: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));

这是最好的方法之一吗?如果是,是哪一个?如果不是,我该怎么办?

【问题讨论】:

    标签: c# linq removeall


    【解决方案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+1th 元素在删除后变为 ith 元素,并且当您增加 i 时,您将跳过它。

      【讨论】:

      • 它实际上是 O(n^3),但我假设缺乏物化只是你的想法;)
      • 是否会使用第二条指令从 subList 中删除项目(如我所写)? :O
      • 你永远不会从 sublist 中删除,如果我没看错的话你也不打算这样做。
      • 你的编辑是对的,我会删除第三个选项,因为它真的错了,如果把它改正会很不可读
      • 当你在一个集合上运行一个 linq 查询时,你不会得到一个数组,你会得到一个对象,当你迭代它时你运行你的实际选择.所以举你的第一个例子。 subList 将只是一个对象,您从主数组中删除项目,然后当您执行 foreach(var item in subList) 时,您什么也得不到,因为条件总是返回 false。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-16
      • 1970-01-01
      • 1970-01-01
      • 2013-07-18
      • 1970-01-01
      相关资源
      最近更新 更多