C#泛型列表Union问题

8
我正在尝试使用“Union”合并两个列表,以便消除重复项。以下是示例代码:
public class SomeDetail
{
    public string SomeValue1 { get; set; }
    public string SomeValue2  { get; set; }
    public string SomeDate { get; set; }
}

public class SomeDetailComparer : IEqualityComparer<SomeDetail>
{
    bool IEqualityComparer<SomeDetail>.Equals(SomeDetail x, SomeDetail y)
    {
        // Check whether the compared objects reference the same data.        
        if (Object.ReferenceEquals(x, y))
            return true;
        // Check whether any of the compared objects is null.        
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;
        return x.SomeValue1 == y.SomeValue1 && x.SomeValue2 == y.SomeValue2;
    }
    int IEqualityComparer<SomeDetail>.GetHashCode(SomeDetail obj)
    {
        return obj.SomeValue1.GetHashCode();
    }
}

List<SomeDetail> tempList1 = new List<SomeDetail>();
List<SomeDetail> tempList2 = new List<SomeDetail>();

List<SomeDetail> detailList = tempList1.Union(tempList2, SomeDetailComparer).ToList();

现在的问题是,我能否使用Union并且仍然获取具有最新日期(使用SomeDate属性)的记录。该记录可以位于tempList1或tempList2中。
提前感谢。
6个回答

8

真正适合此目的的操作是完全外连接Enumerable类有一个内部连接的实现,您可以使用它来查找重复项并选择您喜欢的任何一个。

var duplicates = Enumerable.Join(tempList1, tempList2,  keySelector, keySelector, 
    (item1, item2) => (item1.SomeDate > item2.SomeDate) ? item1 : item2)
    .ToList();

keySelector是一个函数(可以是lambda表达式),它从类型为SomeDetail的对象中提取一个键。现在,要实现完全外连接,请尝试以下操作:

var keyComparer = (SomeDetail item) => new { Value1 = item.SomeValue1,
    Value2 = item.SomeDetail2 };
var detailList = Enumerable.Union(tempList1.Except(tempList2, equalityComparer), 
    tempList2.Except(tempList1, equalityComparer)).Union(
    Enumerable.Join(tempList1, tempList2, keyComparer, keyComparer
    (item1, item2) => (item1.SomeDate > item2.SomeDate) ? item1 : item2))
    .ToList();

equalityComparer 应该是一个实现 IEqualityComparer<SomeDetail> 接口的对象,它需要有效地使用 keyComparer 函数来测试相等性。

如果这样做对您有帮助,请告诉我。


我在SomeDetail类中使用了一个独特的值作为选择器,但是没有返回任何记录。请帮忙看看问题出在哪里?var detailList = Enumerable.Join(tempList1, tempList2, item1 => item1.UniqueKey, item2 => item2.UniqueKey,
(item1, item2) => (item1.SomeDate > item2.SomeDate) ? item1 : item2)
.ToList();
- Ganesha
@Ganesha:你确认至少有一些项目具有相同的UniqueKey值吗? - Noldorin
在你最初的问题中,你是基于表达式 x.SomeValue1 == y.SomeValue1 && x.SomeValue2 == y.SomeValue2 来比较项目的。这意味着你想要选择一组 SomeValue1 和 SomeValue2 作为键。(在 C# 中,最好的选项可能是使用匿名类型。我会更新我的答案并提供代码。) - Noldorin
新的带有键比较器的代码没有返回任何记录?请帮忙一下? - Ganesha
是的,这些列表具有相同的数据。合并结果也很完美(即)它们是唯一的。只是每次运行时日期都会有所不同。我需要最新日期的记录显示在最终合并列表中。 - Ganesha
显示剩余11条评论

1

为什么不直接使用 HashSet<T> 呢?

List<SomeDetail> tempList1 = new List<SomeDetail>();
List<SomeDetail> tempList2 = new List<SomeDetail>();

HashSet<SomeDetail> hs = new HashSet<SomeDetail>(new SomeDetailComparer());

hs.UnionWith(tempList1);
hs.UnionWith(tempList2);

List<SomeDetail> detailList = hs.ToList();

如果我需要唯一的记录,这将起作用。我有一个额外的要求,在合并时,我需要在两个列表之间具有最新日期的记录。 - Ganesha

1

你需要告诉 Union 如何选择重复项中的哪一个。除了编写自己的 Union,我不知道其他方法可以做到这一点。


1

使用标准的Union方法是不行的,但是你可以为List<SomeDetail>创建一个扩展方法Union,并进行特殊处理,因为该方法的签名更加匹配,所以它将被使用。


0
合并通用列表
    public static List<T> MergeListCollections<T>(List<T> firstList, List<T> secondList)
    {
        List<T> merged = new List<T>(firstList);
        merged.AddRange(secondList);
        return merged;
    }

我认为这不会消除重复项,这在问题中已经指明。 - AaronLS

-1

试试这个:

list1.RemoveAll(p => list2.Any(z => z.SomeValue1 == p.SomeValue1 &&
                               z => z.SomeValue2 == p.SomeValue1 &&
                               z => z.SomeDate == p.SomeDate));
var list3 =  list2.Concat<SomeDetail>(list1).ToList();

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