.NET Enumerable Union / Join组合

时间:2012-08-27 01:46:30

标签: c# .net linq enumeration

我正在尝试将两个列表“加入”。两个列表具有相同的类型,并且该类型的每个实例都具有唯一键。如果两个列表中都存在具有相同键的实例,那么我想将两个实例与自定义合并功能合并在一起。最终的项目列表应该包含合并的元素以及首先只在两个列表中的一个列表中的实例。

这类似于UnionJoin,但似乎每个都略有不同。 union会给我正确的密钥列表,但没有设施来合并它们共享相同密钥的实例 - 它只会返回其中一个实例而忽略另一个实例。连接允许我通过提供函数来合并重复的实例,但它只返回两个列表中的元素 - 不是一个或另一个。

我错过了一个很好的内置方法吗?

2 个答案:

答案 0 :(得分:3)

这应该很容易。

如果我可以假设你有merge这样的功能:

Func<T, T, T> merge = (a, b) => /* your result here */;

然后这应该有效:

var intersects = listA.Join(listB, x => x.Id, x => x.Id, (a, b) => merge(a, b));
var excepts = listA.Except(listB).Concat(listB.Except(listA));

var results = intersects.Concat(excepts);

让我知道这是否有效。

答案 1 :(得分:2)

假设列表中没有重复ID,您需要的是外连接。这是一个实现...我不保证最佳性能:

public static class LinqEx
{
    public static IEnumerable<TResult> 
        LeftOuterJoin<TOuter, TInner, TKey, TResult>(
            this IEnumerable<TOuter> outer, 
            IEnumerable<TInner> inner, 
            Func<TOuter, TKey> outerKeySelector, 
            Func<TInner, TKey> innerKeySelector, 
            Func<TOuter, TInner, TResult> resultSelector)
    {
        return outer
            .GroupJoin(
                inner, 
                outerKeySelector, 
                innerKeySelector, 
                (a, b) => new
                {
                    a,
                    b
                })
            .SelectMany(
                x => x.b.DefaultIfEmpty(), 
                (x, b) => resultSelector(x.a, b));
    }

    public static IEnumerable<TResult> 
        FullOuterJoin<TSet1, TSet2, TKey, TResult>(
            this IEnumerable<TSet1> set1, 
            IEnumerable<TSet2> set2, 
            Func<TSet1, TKey> set1Selector, 
            Func<TSet2, TKey> set2Selector, 
            Func<TSet1, TSet2, TResult> resultSelector)
    {
        var leftJoin = set1.
            LeftOuterJoin(
                set2, 
                set1Selector, 
                set2Selector, 
                (s1, s2) => new {s1, s2});
        var rightJoin = set2
            .LeftOuterJoin(
                set1, 
                set2Selector, 
                set1Selector, 
                (s2, s1) => new {s1, s2});
        return leftJoin.Union(rightJoin)
            .Select(x => resultSelector(x.s1, x.s2));

    }
}

这样:

list1.FullOuterJoin(
    list2, 
    list1Item => list1Item.Id,
    list2Item => list2Item.Id,
    (list1Item, list2Item) => {
      if(listItem1!=null && listItem2!=null)
      {
        return merge(listItem1, listItem2);
      }
      return listItem1 ?? listItem2;
    })