Аналитический отдел хочет отчёт по активным игрокам: только те, кто заходил в последние 30 дней, имеет уровень выше 10 и не забанен. Раньше для каждого нового фильтра приходилось добавлять ещё один if внутри цикла.

Задача: Из списка игроков выберите тех, кто соответствует всем трём условиям одновременно. Попробуйте сделать это с помощью Where.

В прошлых уроках мы увидели Where мельком — в составе цепочки. Сегодня разберём его по-настоящему. Это самый используемый метод LINQ — с него начинается почти любой запрос. Если LINQ — это язык запросов, то Where — его главное слово. Давайте начнём с простого. У нас есть список игроков, и мы хотим оставить только магов. Без LINQ мы бы написали так:

var mages = new List<Player>();
foreach (var p in players)
{
    if (p.Class == "Маг")
        mages.Add(p);
}
// 4 строки ради одной мысли: "дай мне только магов"

С Where та же мысль записывается в одну строку:

var mages = players.Where(p => p.Class == "Маг");

Вот и всё. Одна строка вместо четырёх. Но давайте разберём, что тут происходит, потому что в этой строке спрятано несколько важных вещей.

Параметр p => p.Class == "Маг" — это лямбда-выражение. Оно принимает игрока и возвращает true или false. Такую функцию-условие называют предикатом.

Представьте себе охранника на входе в VIP-зону. Он проверяет каждого гостя по списку. Если гость в списке — проходит. Если нет — разворачивается. Предикат — это правило, по которому охранник принимает решение. Where — это сам охранник. Теперь давайте усложним. Аналитический отдел хочет отчёт по активным игрокам: только тех, кто заходил в последние 30 дней, имеет уровень выше 10 и не забанен. Три условия одновременно.

Первый инстинкт — три вложенных if. Но в LINQ условия просто комбинируются через && и || прямо внутри лямбды:

var activePlayers = players.Where(p =>
    p.LastLogin >= DateTime.Now.AddDays(-30)
    && p.Level > 10
    && !p.IsBanned);

Три условия — одна строка фильтрации. Читается как обычное предложение: «где последний вход свежее 30 дней, и уровень больше 10, и не забанен».

А можно ли записать это по-другому? Да — через цепочку нескольких Where:

var activePlayers = players
    .Where(p => p.LastLogin >= DateTime.Now.AddDays(-30))
    .Where(p => p.Level > 10)
    .Where(p => !p.IsBanned);

Результат тот же самый. Производительность тоже одинаковая — LINQ оптимизирует это под капотом.

А вот кое-что, о чём многие не знают. У Where есть перегрузка, которая передаёт в лямбду не только элемент, но и его индекс в коллекции:

// Выбрать элементы на чётных позициях (0, 2, 4...)
var everySecond = players.Where((p, index) => index % 2 == 0);

Это бывает полезно для специфических задач — например, взять каждый третий элемент из лога или выбрать элементы по их позиции. Не самая частая потребность, но когда она возникает — приятно знать, что Where это умеет. Where работает не только со списками объектов. Он работает с любой коллекцией. Вот пример со строкой — выберем только цифры из текста:

string input = "Альберт: уровень 50, золото 1200";

var digits = input.Where(c => char.IsDigit(c));
string numbers = new string(digits.ToArray());
Console.WriteLine(numbers); // "501200"

Строка в C# — это IEnumerable<char>, а значит, все LINQ-методы к ней применимы. Это может показаться неожиданным, но это работает и бывает очень удобно.

А вот пример со словарём — отберём предметы дороже 100 золотых:

var inventory = new Dictionary<string, int>
{
    { "Зелье здоровья", 50 },
    { "Меч огня", 500 },
    { "Щит льда", 300 },
    { "Свиток телепорта", 80 }
};

var expensive = inventory.Where(item => item.Value > 100);

foreach (var item in expensive)
    Console.WriteLine($"{item.Key}: {item.Value} золотых");
// Меч огня: 500 золотых
// Щит льда: 300 золотых

Обратите внимание: при работе со словарём Where перебирает пары KeyValuePair<string, int>. Поэтому в лямбде мы обращаемся к item.Key и item.Value.

И ещё один момент, который стоит запомнить: Where — это фильтрация, не трансформация. Он не меняет элементы — только решает, какие оставить, а какие выбросить. Если нужно изменить форму элементов — для этого есть Select, о котором поговорим в следующем уроке.

Where — метод фильтрации. Принимает предикат (функцию, возвращающую bool) и оставляет только элементы, для которых предикат вернул true.

Условия комбинируются через && и || в одном Where, или через цепочку нескольких Where — результат одинаковый.

Перегрузка Where((element, index) => ...) даёт доступ к индексу элемента.

Where не изменяет исходную коллекцию.

Обсуждение урока

0
Комментарии видны всем. Чтобы участвовать в обсуждении, войдите или зарегистрируйтесь.
Модерация сообщества

Пожаловаться на комментарий

Расскажите модераторам, что именно требует внимания.