2

我正在尝试将两个列表“加入”在一起。两个列表都属于同一类型,并且该类型的每个实例都有一个唯一的键。如果两个列表中都有具有相同键的实例,那么我想使用自定义合并函数将这两个实例合并在一起。项目的最终列表应包含合并的元素以及最初仅位于两个列表之一中的实例。

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

我是否错过了一个很好的内置方法来做到这一点?

4

2 回答 2

4

这应该很容易。

如果我可以假设你有这样的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);

让我知道这个是否奏效。

于 2012-08-27T02:12:01.817 回答
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;
    })
于 2012-08-27T02:00:58.080 回答