Каков "современный" способ найти общие элементы в двух списках <T> объектов?

У меня есть два общих списка, содержащие разные типы, для примера, позвоните им Products и Employees. Я пытаюсь найти Продукты, которые основаны на том же месте, что и Сотрудники, т.е. Где product.SiteId == emp.SiteId

List<Product> lstProds;
List<Employees> lstEmps;

Мой (старый skool) мозг говорит мне использовать цикл forEach, чтобы найти совпадения, но я подозреваю, что есть способ (лучше)/terser/fast?) сделать это с помощью Linq. Может ли кто-нибудь осветить меня? Все примеры, которые я нашел в Интернете, касаются списков примитивов (строк/ints) и не особенно полезны.

Ответ 1

Я бы сказал:

var products = from product in lstProds
               join employee in lstEmps on product.SiteId equals employee.SiteId
               select product;

Однако, если есть несколько сотрудников с одинаковым идентификатором сайта, вы будете получать продукты несколько раз. Вы можете использовать Distinct, чтобы исправить это, или создать набор идентификаторов сайтов:

var siteIds = new HashSet<int>(lstEmps.Select(emp => emp.SiteId));

var products = lstProds.Where(product => siteIds.Contains(product.SiteId));

Предполагая, что SiteId является int - если это анонимный тип или что-то подобное, вам может потребоваться дополнительный метод расширения:

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
    return new HashSet<T>(source);
}

Тогда:

var siteIds = lstEmps.Select(emp => emp.SiteId).ToHashSet();
var products = lstProds.Where(product => siteIds.Contains(product.SiteId));

В качестве альтернативы, если у вас мало сотрудников, это будет работать, но будет относительно медленным:

var products = lstProds.Where(p => lstEmps.Any(emp => p.SiteId == emp.SiteId));

Добавьте вызов ToList к любому из этих подходов, чтобы получить List<Product> вместо IEnumerable<Product>.