C#. LINQ. Как правильно отфильтровать List'ы в List'е?

Есть большой лист из листов, к котором примерно такая структура:

List<CatalogItems> -> List<Catalogs> -> List<SubCategories> -> List<CatalogCategories>

В каждой из них есть лист предыдущего и парам. Visible.

Написал запрос который фильтрует и убирает все те элементы, в которых нет Visible или список пуст уровнем ниже.

    CatalogCategories = CatalogCategories.Where(x => x.SubCategories.Any(z => z.Visible  
&& z.Catalogs.Any(q => q.Visible && q.CatalogItems.Any(w => w.Visible )))).ToList();

Проблема в том, что он убирает запись из CatalogCategories только при условии что все элементы листа соответствуют одному условию, а идея была в том, чтобы просто убрать любой элемент, не подходящий под него)

Я немного запутался в работе внутри пары листов, поэтому буду рад подсказке как стоит правильнее переписать запрос


Ответы (1 шт):

Автор решения: VladD

Поскольку у вас структура данных очевидно рекурсивна, имеет смысл использовать рекурсивные алгоритмы.

Далее, обновление списка — мутирующая операция, следовательно, LINQ для неё не лучший выбор, т. к. LINQ создаёт лишь новые структуры данных, но не должно модифицировать старые.

Таким образом, давайте попробуем вынести общую функциональность в интерфейс. У нас каждый элемент обладаем статусом Visible, списком подэлементов, а также (рекурсивной) функцией «очистки». Получаем вот что:

interface IItem
{
    bool IsVisible { get; }
    IEnumerable<IItem> Subitems { get; }
    void Clear();
}

Теперь, нам нужна абстрактная реализация, чтобы не дублировать код в каждом классе. Получается вот что:

class Item<T> : IItem where T : class, IItem
{
    public List<T> Items { get; } = new();
    public bool IsVisible { get; private set; }
    public IEnumerable<IItem> Subitems => Items;

    public void Clear()
    {
        if (!IsVisible)
            return;
        foreach (var subitem in Subitems)
            subitem.Clear();
        Items.RemoveAll(i => !i.IsVisible || !i.Subitems.Any());
    }
}

(задача со звёздочкой: подумайте, для чего нужен constraint class?)

Все реализации, кроме листовых, можно основывать на этой:

class CatalogCategories : IItem
{
    public bool IsVisible { get; private set; }
    public IEnumerable<IItem> Subitems => Enumerable.Empty<IItem>();
    public void Clear() { }
}

class SubCategories : Item<CatalogCategories> { /* тут остальные методы, если нужно */ }

class Catalogs : Item<SubCategories> { /* и тут */ }

class CatalogItems : Item<Catalogs> { /* и тут */ }

class Root : Item<CatalogItems> { /* ну и тут тоже */ }

Я ввёл ещё один класс Root, чтобы запускать рекурсию из него. Теперь вам нужно просто вызвать root.Clear();.

→ Ссылка