LINQ: RemoveAll и получить элементы удалены

Это самый простой способ удалить элементы, соответствующие некоторому условию из списка, а затем получить эти элементы.

Я могу думать несколькими способами, я не знаю, какой из них лучший:

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

является ли это одним из лучших способов? Если да, то какой? Если нет, то как мне это сделать?

3 ответов


Я бы пошел с первым вариантом для целей удобочитаемости, с примечанием, что вы должны материализовать список первым, или вы потеряете те самые элементы, которые вы пытаетесь выбрать в следующей строке:

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

второй пример-O (n^2) без причины, и последний отлично, но менее читаемый.

Edit: теперь, когда я перечитал ваш последний пример, обратите внимание, что, как это написано прямо сейчас, будет вынимать каждый другой элемент. Вы пропускаете проверку состояния и удаление строка должна быть list.RemoveAt(i--);, потому что i+1th элемент становится i - й элемент после удаления и при увеличении i вы пропускаете его.


мне нравится использовать подход функционального программирования (только создавать новые вещи, не изменять существующие вещи). Одно из преимуществ 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]);

Я искал то же самое. Я хотел сделать некоторые ошибки на предметы, которые я удалял. Поскольку я добавлял целую кучу правил проверки, вызов 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."));