Tru*_*ill 23 c# linq linq-to-objects
我想在一个集合中找到与另一个集合不匹配的所有项目.但是,这些集合的类型不同; 我想编写一个lambda表达式来指定相等性.
一个LINQPad我正在尝试做的例子:
void Main()
{
var employees = new[]
{
new Employee { Id = 20, Name = "Bob" },
new Employee { Id = 10, Name = "Bill" },
new Employee { Id = 30, Name = "Frank" }
};
var managers = new[]
{
new Manager { EmployeeId = 20 },
new Manager { EmployeeId = 30 }
};
var nonManagers =
from employee in employees
where !(managers.Any(x => x.EmployeeId == employee.Id))
select employee;
nonManagers.Dump();
// Based on cdonner's answer:
var nonManagers2 =
from employee in employees
join manager in managers
on employee.Id equals manager.EmployeeId
into tempManagers
from manager in tempManagers.DefaultIfEmpty()
where manager == null
select employee;
nonManagers2.Dump();
// Based on Richard Hein's answer:
var nonManagers3 =
employees.Except(
from employee in employees
join manager in managers
on employee.Id equals manager.EmployeeId
select employee);
nonManagers3.Dump();
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Manager
{
public int EmployeeId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
以上工作,将返回员工账单(#10).但它看起来并不优雅,而且对于较大的集合来说可能效率低下.在SQL中,我可能会执行LEFT JOIN并查找第二个ID为NULL的项目.在LINQ中执行此操作的最佳做法是什么?
编辑:更新以防止依赖于Id等于索引的解决方案.
编辑:添加了cdonner的解决方案 - 任何人有什么更简单的?
编辑:在Richard Hein的答案中添加了一个变体,我目前最喜欢的.感谢大家的一些优秀答案!
Ric*_*ein 30
这与其他一些示例几乎相同,但代码较少:
employees.Except(employees.Join(managers, e => e.Id, m => m.EmployeeId, (e, m) => e));
Run Code Online (Sandbox Code Playgroud)
然而,它并不比employees.Where(e =>!managers.Any(m => m.EmployeeId == e.Id))或原始语法更简单.
/// <summary>
/// This method returns items in a set that are not in
/// another set of a different type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TOther"></typeparam>
/// <typeparam name="TKey"></typeparam>
/// <param name="items"></param>
/// <param name="other"></param>
/// <param name="getItemKey"></param>
/// <param name="getOtherKey"></param>
/// <returns></returns>
public static IEnumerable<T> Except<T, TOther, TKey>(
this IEnumerable<T> items,
IEnumerable<TOther> other,
Func<T, TKey> getItemKey,
Func<TOther, TKey> getOtherKey)
{
return from item in items
join otherItem in other on getItemKey(item)
equals getOtherKey(otherItem) into tempItems
from temp in tempItems.DefaultIfEmpty()
where ReferenceEquals(null, temp) || temp.Equals(default(TOther))
select item;
}
Run Code Online (Sandbox Code Playgroud)
我不记得我在哪里找到这种方法.