#c# #list #linq #dictionary
#c# #Список #linq #словарь
Вопрос:
У меня есть этот словарь Dictionary<string, List<Payments>>
, в котором содержатся сотрудники, у каждого сотрудника есть список платежей. Класс Payments имеет строковое свойство с именем PayCategoryId . Я хочу отфильтровать этот словарь и получить только сотрудников с платежами, имеющими определенные значения PayCategoryId, и для каждого сотрудника только эти платежи. Я почти уверен, что этого можно достичь с помощью LINQ, но у меня почти нулевой опыт работы с LINQ, поэтому нужна ваша помощь.
Исходный (нефильтрованный) словарь содержит 76 элементов (сотрудников). Сотрудник, который я буду использовать в качестве примера, имеет 27 платежей, некоторые из которых имеют требуемый идентификатор PayCategoryId.
Что я сделал:
- Список с требуемым идентификатором PayCategoryId:
var payCategoriesID = new List<string> (){ "a", "b", "c" };
- Частично фильтровать словарь с помощью этого LINQ (я уверен, что это беспорядок, но работает!):
var result = dict.Where(o => o.Value.Where(x => payCategoriesID.Contains(x.PayCategoryId)).Any()).ToDictionary(mc => mc.Key, mc => mc.Value);
- Полуфильтрованный результирующий словарь содержит только 34 элемента. Сотрудники, не имеющие платежей с требуемым идентификатором PayCategoryId, были отфильтрованы. Но в моем примере у сотрудника все еще есть все 27 платежей в списке. Мне нужно, чтобы его список тоже был отфильтрован и имел только платежи, имеющие PayCategoryId = один из идентификаторов из списка payCategoriesID.
Конечно, пример employee — это всего лишь пример, все сотрудники должны быть отфильтрованы.
Не могли бы вы помочь, пожалуйста?
Адриан
Ответ №1:
Вы можете использовать Select()
для проецирования в перечислимый анонимный тип и получать только нужное вам значение, а затем создавать словарь.
var match = dict
.Select(kv => new
{
Employee = kv.Key,
Payments = kv.Value.Where(p => payCategoriesID.Contains(p.PayCategoryId))
})
.Where(emp => emp.Payments.Any())
.ToDictionary(k => k.Employee, v => v.Payments);
На первом шаге создается объект с ключом и отфильтрованными значениями, затем Where()
удаляются пустые списки. Таким образом, вы выполняете итерацию по списку идентификаторов только один раз (для каждого элемента в словаре).
Кроме того, на самом деле это не связано, но Any()
метод имеет перегрузку, которая принимает предикат, поэтому вместо
o.Value.Where(x => payCategoriesID.Contains(x.PayCategoryId)).Any()
вы можете сделать напрямую
o.Value.Any(x => payCategoriesID.Contains(x.PayCategoryId))
То же самое относится и к другим методам LINQ, таким как Count()
, First()
, FirstOrDefault()
, Last()
и другим.
Комментарии:
1. Элегантный, лаконичный и объясненный. Спасибо, @ColinD!
Ответ №2:
var result = dict.Where(o => o.Value.Where(x => payCategoriesID.Contains(x.PayCategoryId)).Any()).ToDictionary(mc => mc.Key, mc => mc.Value.Where(t=> payCategoriesID.Contains(t.PayCategoryId)));
Комментарии:
1. Бинго! И ДОХ! 🙂 Большое спасибо!