12

这是从列表中删除与某些条件匹配的项目然后获取这些项目的最简单方法。

我可以从几个方面考虑,我不知道哪个是最好的:

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));

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

4

4 回答 4

8

我喜欢使用函数式编程方法(只做新的东西,不要修改现有的东西)。一个优点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]);
于 2012-04-16T17:54:16.377 回答
5

出于可读性目的,我会选择第一个选项,并注意您应该首先实现列表,否则您将丢失您尝试在下一行选择的项目:

var sublist = list.Where(x => x.Condition).ToArray();
list.RemoveAll(x => x.Condition);

第二个例子是 O(n^2) 无缘无故,最后一个很好,但可读性较差。

编辑:现在我重新阅读了您的最后一个示例,请注意,现在写的内容将删除所有其他项目。您缺少条件检查,而删除行实际上应该是list.RemoveAt(i--);因为第 th 元素在删除后i+1成为第ith 元素,并且当您递增时,i您正在跳过它。

于 2012-04-16T17:09:38.513 回答
5

第一个选项很好,但它会导致两次收集。您可以通过在谓词中执行附加逻辑来一次性完成:

        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));
于 2019-07-28T14:13:06.350 回答
1

我一直在寻找同样的东西。我想对要删除的项目进行一些错误记录。由于我添加了一大堆验证规则,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."));
于 2018-04-24T15:51:49.140 回答