合并两个列表并去重,将结果输出到第三个列表中。我的尝试没有成功。

5

当我需要比较两个列表并生成一个包含所有唯一项的第三个列表时,我总是遇到问题。我经常需要执行这个操作。

尝试用一个简单的例子重现此问题。

我是否遗漏了什么?感谢任何建议。

期望的结果:

   Name= Jo1 Surname= Bloggs1 Category= Account
   Name= Jo2 Surname= Bloggs2 Category= Sales
   Name= Jo5 Surname= Bloggs5 Category= Development
   Name= Jo6 Surname= Bloggs6 Category= Management
   Name= Jo8 Surname= Bloggs8 Category= HR
   Name= Jo7 Surname= Bloggs7 Category= Cleaning

class Program
{
    static void Main(string[] args)
    {
          List<Customer> listOne = new List<Customer>();
        List<Customer> listTwo = new List<Customer>();

        listOne.Add(new Customer { Category = "Account", Name = "Jo1", Surname = "Bloggs1" });
        listOne.Add(new Customer { Category = "Sales", Name = "Jo2", Surname = "Bloggs2" });
        listOne.Add(new Customer { Category = "Development", Name = "Jo5", Surname = "Bloggs5" });
        listOne.Add(new Customer { Category = "Management", Name = "Jo6", Surname = "Bloggs6" });



        listTwo.Add(new Customer { Category = "HR", Name = "Jo8", Surname = "Bloggs8" });
        listTwo.Add(new Customer { Category = "Sales", Name = "Jo2", Surname = "Bloggs2" });
        listTwo.Add(new Customer { Category = "Management", Name = "Jo6", Surname = "Bloggs6" });
        listTwo.Add(new Customer { Category = "Development", Name = "Jo5", Surname = "Bloggs5" });
        listTwo.Add(new Customer { Category = "Cleaning", Name = "Jo7", Surname = "Bloggs7" });


    List<Customer> resultList = listOne.Union(listTwo).ToList();//**I get duplicates why????**

        resultList.ForEach(customer => Console.WriteLine("Name= {0} Surname= {1} Category= {2}", customer.Name, customer.Surname, customer.Category));
        Console.Read();

        IEnumerable<Customer> resultList3 = listOne.Except(listTwo);//**Does not work**

        foreach (var customer in resultList3)
        {
            Console.WriteLine("Name= {0} Surname= {1} Category= {2}", customer.Name, customer.Surname, customer.Category);
        }

        **//Does not work**
        var resultList2 = (listOne
                       .Where(n => !(listTwo
                           .Select(o => o.Category))
                           .Contains(n.Category)))
                       .OrderBy(n => n.Category);

        foreach (var customer in resultList2)
        {
            Console.WriteLine("Name= {0} 
                             Surname= {1} 
                             Category= {2}", 

customer.Name, customer.Surname, customer.Category); } Console.Read();

客户姓名,客户姓氏,客户类别); } Console.Read();
  }
}

public class Customer
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public string Category { get; set; }
}
4个回答

18

您是否可以使用 ConcatDistinct LINQ 方法来实现这个功能呢?

List<Customer> listOne;
List<Customer> listTwo;

List<Customer> uniqueList = listOne.Concat(listTwo).Distinct().ToList(); 

如果有必要,您可以使用带有IEqualityComparer的Distinct()重载来创建自定义的相等比较。


2
谢谢您的回复。这与List<Customer> resultList = listOne.Union(listTwo).ToList();相同吗?我仍然得到重复项,可能是我的客户示例有问题。 - user9969
1
我知道这篇文章已经有些年头了,但是对于任何面临相同问题的人来说:你的客户可能是两个不同的对象,但名称相同。如果你想通过名称(或任何其他属性)进行比较,你应该使用一个比较器,就像这个帖子中所描述的那样。 - Bahamut
除了需要明确定义相等性之外,如接受的答案所述,如果listTwo中包含在listOne中的客户,则结果将不是唯一的。一个简单的联合就足够了,因为Union是一个隐式的Distinct。 - Gert Arnold

15
问题的关键在于Customer对象没有实现.Equals()方法。如果你重写.Equals (和.GetHashCode()),那么.Distinct()会使用它来消除重复项。但是,如果你不拥有Customer实现,添加.Equals可能不是一个选项。
另一种选择是向.Distinct()传递一个自定义的IEqualityComparer。这让你可以根据传入的比较器以不同的方式比较对象。
另一种选择是按重要字段分组并从该组中取任何一个项目(因为GroupBy在这种情况下充当.Equals)。这需要编写最少的代码。
例如:
    var result = listOne.Concat(listTwo)
        .GroupBy(x=>x.Category+"|"+x.Name+"|"+x.Surname)
        .Select(x=>x.First());

通常我使用独特的分隔符将字段组合起来,以便应该不同的两个项目不会意外地组合到同一个键中。 例如: {Name=abe, Surname=long}{Name=abel, Surname=ong} 如果不使用分隔符,则都会得到 GroupBy 键 "abelong"

这样做可以得到您想要的结果。


1
最好的选择是实现IEqualityComparer接口,并将其用于Union或Distinct方法中,就像我在本文末尾所写的那样。 http://blog.santiagoporras.com/combinar-listas-sin-duplicados-linq/
  • IEqualityComparer的实现
public class SaintComparer : IEqualityComparer<Saint>
{
    public bool Equals(Saint item1, Saint item2)
    {
       return item1.Name == item2.Name;
    }
     
    public int GetHashCode(Saint item)
    {
      int hCode = item.Name.Length;
      return hCode.GetHashCode();
    }
}

使用比较器。
var unionList = list1.Union(list2, new SaintComparer());


解释为什么这就是所需的:Union 是一个隐式的 Distinct。这应该是被接受的答案。 - Gert Arnold

0

我遇到了一个类似的问题,我有两个非常大的列表,其中包含随机字符串。

我编写了一个递归函数,返回一个包含唯一字符串的新列表。我比较了两个列表,每个列表都有100k个随机字符串(可能存在重复),每个字符串由abcdefghijklmnopqrstuvwxyz1234567890中的6个字符组成,用时约为230毫秒。我只测量了给定的函数。

我希望这对某人有价值。

测试运行的图像

makeCodesUnique(List<string> existing, List<string> newL)
{
    // Get all duplicate between two lists
    List<string> duplicatesBetween = newL.Intersect(existing).ToList();

    // Get all duplicates within list
    List<string> duplicatesWithin = newL.GroupBy(x => x)
    .Where(group => group.Count() > 1)
    .Select(group => group.Key).ToList();

    if (duplicatesBetween.Count == 0 && duplicatesWithin.Count == 0)
    {
        // Return list if there are no duplicates
        return newL; 
    }
    else
    {
        if (duplicatesBetween.Count != 0)
        {
            foreach (string duplicateCode in duplicatesBetween)
            {
                newL.Remove(duplicateCode);
            }

            // Generate new codes to substitute the removed ones
            List<string> newCodes = generateSomeMore(duplicatesBetween.Count);
            newL.AddRange(newCodes);
            makeCodesUnique(existing, newL);
        }
        else if (duplicatesWithin.Count != 0)
        {
            foreach (string duplicateCode in duplicatesWithin)
            {
                newL.Remove(duplicateCode);
            }
            List<string> newCodes = generateSomeMore(duplicatesWithin.Count);
            new.AddRange(newCodes);
            makeCodesUnique(existing, newL);
        }
    }
    return newL;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接