【问题标题】:Remove duplicates while merging lists using Union in LINQ在 LINQ 中使用 Union 合并列表时删除重复项
【发布时间】:2016-05-14 02:16:52
【问题描述】:

我正在尝试在LinqPad 中使用list.Union 合并两个列表,但我无法让它工作并想检查我的理解是否正确。

鉴于这个简单的类:

public class Test 
{
   public int Id { get; set;}
   public int field1 { get; set; }

   public bool Equals(Test other)
   {        
      return this.Id.Equals(other.Id);
   }
}

两个列表如下填充:

List<Test> list = new List<Test>();
list.Add( new Test { Id = 1, field1 = 1});
list.Add( new Test { Id = 1, field1 = 2});
list.Add( new Test { Id = 2, field1 = 3});
list.Add( new Test { Id = 2, field1 = 4});

List<Test> list2 = new List<Test>();
list2.Add( new Test { Id = 1, field1 = 1});
list2.Add( new Test { Id = 1, field1 = 2});
list2.Add( new Test { Id = 2, field1 = 3});
list2.Add( new Test { Id = 2, field1 = 4});

然后我尝试:var mergedList = list.Union(list2).ToList(); 并使用简单的foreach 循环输出数据并获得此输出:

ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4
ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4

我的印象是Union 应该删除重复项以返回:

ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4

我做错了什么还是我误解了?

另外,它是否应该在不显式覆盖Test 类中的Equals 方法的情况下工作?

谢谢

【问题讨论】:

标签: c# linq


【解决方案1】:

在您的情况下,您只需定义一些方法,LINQ 对此一无所知。这就像创建方法 bool HeyEquateMeWith(Test other) 并期望 LINQ 在执行集合操作时调用它。

您需要如下定义您的类(覆盖 ObjectEqualsGetHashCode 方法):

public class Test 
{
   public int Id { get; set;}
   public int field1 { get; set; }  

   public override bool Equals(object other) //note parameter is of type object
   {        
        Test t = other as Test;
        return (t != null) ? Id.Equals(t.Id) : false;
   }

   public override int GetHashCode()
   {
        return Id.GetHashCode();
   }
}

现在Union 将调用您覆盖的EqualsGetHashCode 方法。当您覆盖 Equals 方法时,您还应该 ALWAYS 覆盖 GetHashCode

【讨论】:

  • 谢谢。是的,我想我可以将 GetHashCOde 作为示例,但它是必需的。 Union 的默认比较器是如何工作的,还是我总是需要覆盖 Equals 和 GethashCode?​​span>
  • @davy 默认比较器将查看对象身份(与 == 运算符相同的功能)。如果将Test 类更改为struct,默认相等将具有值语义(比较两个结构的内容,包括Idfield1
  • 我明白了。非常感谢您的帮助。
【解决方案2】:

如果对默认比较器不满意,您可以尝试类似的方法(反过来,它使用 @IlyaIvanov 提到的 GetHashCode 方法):

// get all items that "other than in first list", so Where() and Any() are our filtering expressions
var delta = list2.Where(x2 => !list.Any(x1 => (x1.Id == x2.Id) && (x1.field1 == x2.field1)));

// now let merge two enumerables that have nothing "equal" between them
var merged = list.Union(delta).ToList();

【讨论】:

    【解决方案3】:

    你可以创建一个实现类

    IEqualityComparer<Test>
    

    这个类是否定义了 Equals 和 GetHashCode 之后,您可以将此比较器传递给您的 Union 方法 就这样:

    public class MyComparer:IEqualityComparer<Test>{
    //Equals and GetHashCode
    }
    
    var mergedList = list.Union(list2, new MyComparer()).ToList();
    

    【讨论】:

    • 这适用于 LINQ to Objects,但不支持 LINQ to Entities。我很难找到这一点。
    【解决方案4】:

    只是想把这个留给那些仍然无法得到它的人。我发现这篇文章对继承自 IEqualityComparer 的比较类非常有用 http://alicebobandmallory.com/articles/2012/10/18/merge-collections-without-duplicates-in-c

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-22
      相关资源
      最近更新 更多