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