【问题标题】:Lambda expression to contrast different objects用于对比不同对象的 Lambda 表达式
【发布时间】:2012-08-08 01:41:18
【问题描述】:

我有两个列表,一个是联系人,另一个是员工。我想生成不是员工的联系人子集。以下表达式似乎可以告诉我有一些不是员工的联系人,现在我需要一个符合该条件的联系人列表:

if(myContacts.Select(c=>c.contactID).Except(employees.Select(e=>e.contactID)).Any()
{
   //get the subset of contacts and do stuff to them....
}

TIA!

罗恩

【问题讨论】:

  • 问题:联系人和员工是相同的班级类型还是不同的(影响答案)?即它们都是Person 还是一些这样的对象序列?或者是一个Contact和一个Employee,等等......
  • 如果它们是相同的类型(或两者都与他们选择contactID 的基类相关,那么您可以使用EqualityComparer,就像我在下面的回答一样。
  • 对不起,应该指定这些类型是不同的类型。他们共享一个共同的 ID。它们也是内存中的列表。
  • 别担心,然后更新我的答案

标签: c# .net lambda


【解决方案1】:
 var nonEmployees = contacts.Where(w=> ! employees.Any(e=>e.contactID == w.contactID)).ToList();

 var nonEmployees = contacts.Where(w=> ! employees.Select(s=>s.contactID).Contains(w.contactID) ).ToList();

【讨论】:

  • @JamesMichaelHare:我不确定我们能否利用所提供的信息做得更好?
  • 我们可以创建一个自定义的 EqualityComaparer,就像@JamesMichaelHare 建议的那样,假设 Contact 和 Employees 至少来自同一个基类......但是,如果列表很小并且在内存中,则不会比较器会更贵吗?
  • 是的,相对较小的内存列表。谢谢,这就是我正在尝试的。我还在学习 lambda 表达式(显然),但我已经用我的面包切片机换了它们.....
  • 仍然,包含在序列上比先将其放入哈希集要重。这个概念很明确,可以从 HashSet 中受益以提高速度。
【解决方案2】:

如果您的employeescontacts 序列是相同类型的对象(或者都继承自相同类型,例如Contact),那么您可以创建一个EqualityComparer 实例:

public class ContactComparer : EqualityComparer<Contact>
{
    public override bool Equals(Contact x, Contact y)
    {
        if (ReferenceEquals(x, y)) return true;

        return x != null && y != null && x.ContactId == y.ContactId;
    }

    public override int GetHashCode(Contact obj)
    {
        if (obj == null) throw new ArgumentNullException("obj");

        // assuming string
        return (obj.ContactId ?? "").GetHashCode();
    }
}

这会让你这样做,因此你可以直接返回结果而不必重复查询:

var contactsNotEmployees = myContacts.Except(employees, new ContactComparer()).ToList();

更新:正如您的评论所示,Contact 和 Employee 是不同的类型,您可以考虑使用 ContactId 属性创建一个接口来创建一个共同的纽带。

或者,我建议将您的 except 结果导出到 HashSet,然后使用 HashSet 中的 Contains(),这是 O(1) 效率(而不是序列上的 Contains(),即O(n) 效率):

 // get hash set of contact-only IDs
 var except = new HashSet<int>(contacts
      .Select(c => c.ContactId)
      .Except(emplopyees.Select(e => e.ContactId)));

 // get the objects for those IDs
 var others = contacts.Where(c => except.Contains(c.ContactId)).ToList();

比较使用序列 Contains()HashSet Contains() 的结果,对于小列表(15 项),您的速度大约快 50%,对于较长的列表甚至更快。

不管怎样,把它扔掉,因为序列 (IEnumerable&lt;T&gt;) 上的 Contains() 相对较慢...

我对这两个解决方案进行了超过 1,000,000 次迭代,并得到:

 HashSet With Contains() took: 1429 ms, 0.001429 ms/item.
 Sequence With Contains() took: 3386 ms, 0.003386 ms/item.

【讨论】:

  • James,这是一个非常好的例程,但不幸的是,我给你的规格不完整——它们是不同的类型。
  • @Ron:已更新以适合您的规格。我担心使用序列Contains() 的主要问题是它相对较慢 - O(n) 线性时间 - 这就是为什么如果你想要这样的东西,你可以使用 HashSet 来代替。
  • 是的,我将不得不处理数据。显然,这两种方法都有效,但这绝对值得额外 10 分钟的编码。感谢您的帮助。
【解决方案3】:

这不是您想要的吗?

myContacts.Select(c=>c.contactID)
          .Except(employees.Select(e=>e.contactID)
          .ToList()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-12
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多