具有相同条目但不具有相同计数的两个List之间的差异

时间:2014-02-28 13:06:38

标签: c# list difference

我在比较两个列表时遇到了问题。我有两个海关对象列表,我希望它们之间有区别,然后计算结果的数量。这是一个例子:

这是我的自定义类:

public class Contact
{
    public String FirstName { get; set; }
    public String LastName { get; set; }
    public bool IsAdmin { get; set; }
}

在我的应用程序中,当我在两个List It之间产生差异时听起来像这样:

List<Contact> Difference =
    List1.Where(Second =>
        !List2.Any(First =>
            First.FirstName == Second.FirstName
            && First.LastName == Second.LastName
            && First.IsAdmin == Second.IsAdmin))
    .ToList();

这个方法给出了与条件匹配的结果,所以我可以对结果进行分组和计数,除非我有这样的结果:

List<Contact> List1 = new List<Contact>
{
    new Contact { Firstname = "Tom", LastName = "Smith", IsAdmin = true },
    new Contact { Firstname = "Tom", LastName = "Smith", IsAdmin = true },
    new Contact { Firstname = "Bob", LastName = "Smith", IsAdmin = false },
    new Contact { Firstname = "Vincent", LastName = "Smith", IsAdmin = false }
};

List<Contact> List2 =new List<Contact>
{ 
    new Contact { Firstname = "Tom", LastName = "Smith", IsAdmin = true},
    new Contact { Firstname = "Bob", LastName = "Smith", IsAdmin = false}
};

当我运行我的方法时,我得到1个结果:

new Contact { Firstname = "Vincent", LastName = "Smith", IsAdmin = false }

因为它符合条件 但我希望这是结果:

new Contact { Firstname = "Tom", LastName = "Smith", IsAdmin = true}
new Contact { Firstname = "Vincent", LastName = "Smith", IsAdmin = false }

你怎么能让它变得可能?

编辑:工作方法:

var groups1 = List1
    .GroupBy(c => new { c.Firstname, c.LastName, c.IsAdmin });
var groups2 = List2
    .GroupBy(c => new { c.Firstname, c.LastName, c.IsAdmin });

var diffs = from g1 in groups1
            join g2 in groups2
            on g1.Key equals g2.Key into gj
            from g2 in gj.DefaultIfEmpty(Enumerable.Empty<Contact>())
            where g1.Count() > g2.Count()
            select new { g1, g2 };
List<Contact> allDiffs = diffs
    .SelectMany(x => x.g1.Take(x.g1.Count() - x.g2.Count()))
    .ToList();

5 个答案:

答案 0 :(得分:1)

如果您需要两个列表中的公共项目,请删除negation operator

List<Contact> Difference =
Contact_List2.Where(Second =>
    Contact_List1.Any(First =>
        First.FirstName == Second.FirstName
        && First.LastName == Second.LastName
        && First.IsAdmin == Second.IsAdmin))
.ToList();

答案 1 :(得分:1)

也许你想要list1中但不在list2中的所有项目,即使是list2中但不是相同数量的项目,请尝试:

var groups1 = List1
    .GroupBy(c => new { c.Firstname, c.LastName, c.IsAdmin });
var groups2 = List2
    .GroupBy(c => new { c.Firstname, c.LastName, c.IsAdmin });

var diffs = from g1 in groups1
            join g2 in groups2
            on g1.Key equals g2.Key into gj
            from g2 in gj.DefaultIfEmpty(Enumerable.Empty<Contact>())
            where g1.Count() > g2.Count()
            select new { g1, g2 };
List<Contact> allDiffs = diffs
    .SelectMany(x => x.g1.Take(x.g1.Count() - x.g2.Count()))
    .ToList();

(编辑:我希望有一种更简单的方法,但它有效)

答案 2 :(得分:0)

你真正想要的是一个交集,LINQ可以使用Intersect方法,但要使用自定义对象,你需要实现IEquatable<T>(或实现自己的自定义{{} 1}})。

这样您就可以简单地致电IEqualityComparer<T>,了解您的代码目前失败的原因......

var difference = List2.Intersect(List1)

您要求List<Contact> Difference = Contact_List2.Where(Second => !Contact_List1.Any(First => First.FirstName == Second.FirstName && First.LastName == Second.LastName && First.IsAdmin == Second.IsAdmin)) .ToList(); 中的List2中的所有记录List1中的不是 - 您想要的是List2中的所有记录{strong}也是<{1}}中的。

要修复,只需从List1检查中删除!(非)运算符即可。

Any

答案 3 :(得分:0)

或者,您可以将Enumerable.Intersect与自定义相等比较器一起使用。它将生成两个列表中找到的IEnumerable<T>个项目。你会这样使用它:

var matching = List1.Intersect(List2, new ContactComparer());

自定义比较器(无耻地提升并改编自MSDN docs):

public sealed class ContactComparer : IEqualityComparer<Contact>
{
    public bool Equals(Contact x, Contact y)
    {
        if (Object.ReferenceEquals(x, y)) return true;

        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        return x.FirstName == y.FirstName && 
               x.LastName == y.LastName &&
               x.IsAdmin == y.IsAdmin;
    }

    public int GetHashCode(Contact contact)
    {
        if (Object.ReferenceEquals(contact, null)) return 0;

        //Get hash code for the Name field if it is not null. 
        int hashContactFirst = contact.FirstName == null ? 0 : contact.FirstName.GetHashCode();

        //Get hash code for the Code field. 
        int hashContactLast = contact.LastName == null ? 0 : contact.LastName.GetHashCode()

        int hashAdmin = contact.IsAdmin.GetHashCode();

        return hashContactFirst ^ hashContactLast ^ hashAdmin;
    }
}

我个人赞成这条路线,因为它创造了更易读的linq,通过快速浏览更容易理解。

答案 4 :(得分:0)

我不确定单独使用LINQ是多么容易。我认为下面的代码给出了你想要的结果。当然,它实际上正在改变List1,因此如果您需要原始列表,则需要处理副本。

        foreach (Contact contact2 in List2)
        {
            List1.Remove(List1.FirstOrDefault(contact1 => contact1.FirstName == contact2.FirstName
                                                          && contact1.LastName == contact2.LastName
                                                          && contact1.IsAdmin == contact2.IsAdmin));
        }