【问题标题】:Find object data duplicates in List of objects在对象列表中查找对象数据重复项
【发布时间】:2017-05-24 21:25:40
【问题描述】:

使用 c# 3 和 .Net Framework 3.5,我有一个 Person 对象

public Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int SSN { get; set; }
}

我有一份他们的清单:

List<Person> persons = GetPersons();

如何获取列表中 SSN 不唯一的人员中的所有人员对象,并将它们从人员列表中删除,理想情况下将它们添加到另一个名为“List&lt;Person&gt; dupes”的列表中?

原始列表可能如下所示:

persons = new List<Person>();
persons.Add(new Person { Id = 1, 
                         FirstName = "Chris", 
                         LastName="Columbus", 
                         SSN=111223333 }); // Is a dupe
persons.Add(new Person { Id = 1, 
                         FirstName = "E.E.", 
                         LastName="Cummings", 
                         SSN=987654321 });
persons.Add(new Person { Id = 1, 
                         FirstName = "John", 
                         LastName="Steinbeck", 
                         SSN=111223333 }); // Is a dupe
persons.Add(new Person { Id = 1, 
                         FirstName = "Yogi", 
                         LastName="Berra", 
                         SSN=123456789 }); 

最终的结果将是 Cummings 和 Berra 在原始人员列表中,而 Columbus 和 Steinbeck 在一个名为 dupes 的列表中。

非常感谢!

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    这将为您提供重复的 SSN:

    var duplicatedSSN =
        from p in persons
        group p by p.SSN into g
        where g.Count() > 1
        select g.Key;
    

    重复的列表如下:

    var duplicated = persons.FindAll( p => duplicatedSSN.Contains(p.SSN) );
    

    然后只需遍历重复项并删除它们。

    duplicated.ForEach( dup => persons.Remove(dup) ); 
    

    【讨论】:

    • 您的解决方案很接近。 duplicated = persons.FindAll(duplicatedSSN.Contains(p =&gt; p.SSN); 行无效。查看我的答案,看看我纠正了什么以获得答案。
    【解决方案2】:

    感谢 gcores 让我走上了正确的道路。这是我最终做的:

    var duplicatedSSN =
        from p in persons
        group p by p.SSN into g
        where g.Count() > 1
        select g.Key;
    
    var duplicates = new List<Person>();
    
    foreach (var dupeSSN in duplicatedSSN)
    {
        foreach (var person in persons.FindAll(p => p.SSN == dupeSSN))
            duplicates.Add(person);
    }
    
    duplicates.ForEach(dup => persons.Remove(dup));
    

    【讨论】:

    • 对不起,线路错了。应该说重复 = people.FindAll(p => duplicatedSSN.Contains(p.SSN));。我已经编辑了答案。
    【解决方案3】:
    List<Person> actualPersons = persons.Distinct().ToList();
    List<Person> duplicatePersons = persons.Except(actualPersons).ToList();
    

    【讨论】:

    • 这不起作用,因为 Distinct 查看所有数据。我只是想比较 SSN 并在那个领域寻找骗子。
    【解决方案4】:

    基于上述@gcores 的建议。

    如果要将重复 SSN 的单个对象添加回人员列表,请添加以下行:

    IEnumerable<IGrouping<string, Person>> query = duplicated.GroupBy(d => d.SSN, d => d);
    
            foreach (IGrouping<string, Person> duplicateGroup in query)
            {
                persons.Add(duplicateGroup .First());
            }
    

    我的假设是,您可能只想删除重复值减去重复值源自的原始值。

    【讨论】:

      【解决方案5】:

      如果你像这样实现 IComparable:

      int IComparable<Person>.CompareTo(Person person)
      {
          return this.SSN.CompareTo(person.SSN);
      }
      

      然后进行如下比较:

      for (Int32 i = 0; i < people.Count; i++)
      {
          for (Int32 j = 1; j < items.Count; j++)
          {
              if (i != j && items[i] == items[j])
              {
                  // duplicate
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        遍历列表并保留 SSN/count 对的 Hashtable。然后枚举您的表并删除与 SSN 计数 > 0 的 SSN 匹配的项目。

        Dictionary<string, int> ssnTable = new Dictionary<string, int>();
        
        foreach (Person person in persons)
        {
           try
           {
              int count = ssnTable[person.SSN];
              count++;
              ssnTable[person.SSN] = count;
           }
           catch(Exception ex)
           {
               ssnTable.Add(person.SSN, 1);
           }
        }
        
        // traverse ssnTable here and remove items where value of entry (item count) > 1
        

        【讨论】:

          【解决方案7】:

          persons 必须是List&lt;Person&gt; 吗?如果是Dictionary&lt;int, Person&gt; 会怎样?

          var persons = new Dictionary<int, Person>();
          
          ...
          
          // For each person you want to add to the list:
          var person = new Person
          {
            ...
          };
          
          if (!persons.ContainsKey(person.SSN))
          {
            persons.Add(person.SSN, person);
          }
          
          // If you absolutely, positively got to have a List:
          using System.Linq;
          List<Person> personsList = persons.Values.ToList();
          

          如果您正在使用 Person 的唯一实例(而不是可能碰巧具有相同属性的不同实例),您可能会使用 HashSet 获得更好的性能。

          【讨论】: