左连接两个列表并使用Linq从右侧维护一个属性

时间:2015-11-27 16:25:19

标签: c# linq

我有2个相同类型的列表。左侧列表:

var leftList = new List<Person>();
leftList.Add(new Person {Id = 1, Name = "John", Changed = false});
leftList.Add(new Person {Id = 2, Name = "Alice", Changed = false});
leftList.Add(new Person {Id = 3, Name = "Mike", Changed = false});

正确的清单:

var rightList = new List<Person>();
rightList.Add(new Person {Id = 1, Name = "John", Changed = false});
rightList.Add(new Person {Id = 3, Name = "Mike", Changed = true});
rightList.Add(new Person {Id = 4, Name = "Joshi", Changed = true});

我想要左连接,但是使用右侧Changed属性上的值。像这样:

{Id = 1, Name = "John", Changed = false}
{Id = 2, Name = "Alice", Changed = false}
{Id = 3, Name = "Mike", Changed = true} // <-- true from the rightList

为此,我无法使用简单的Left Join,也无法使用Concat with GroupBy

如何使用linq执行此操作?感谢。

4 个答案:

答案 0 :(得分:9)

这看起来像一个非常标准的左外连接场景。

我总是将此扩展方法保留为左外连接方便,所以我不必查看如何使用讨厌的查询语法(或者记住wtf一个GroupJoin)...

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));
    }
}

现在你可以:

leftList.LeftOuterJoin(
     rightList, 
     lft => lft.Id,
     rgt => rgt.Id,
     (lft, rgt) => new Person{Id = lft.Id, 
                              Name = lft.Name, 
                              Changed = rgt == null ? lft.Changed : rgt.Changed})

答案 1 :(得分:3)

为什么不尝试这样的解决方案:

var query = (from left in leftList
    join right in rightList on left.Id equals right.Id into joinedList
    from sub in joinedList.DefaultIfEmpty()
    select new Person { 
        Id = left.Id,
        Name = left.Name,
        Changed = sub == null ? left.Changed : sub.Changed }).ToList();

答案 2 :(得分:1)

另一种方法是:

//Step 1: Merge the lists while selecting the matching objects from rightList using Last()
var mergedList = leftList.Concat(rightList)
 .GroupBy(x => x.Id)
 .Select(x => x.Last());

//Step 2: Do a inner join between mergedList and leftList to get a left join result as originally required.
var innerJoinQuery = from mPerson in mergedList
                     join leftPerson in leftList on mPerson.Id equals leftPerson.Id
                     select new { Id = leftPerson.Id, Name = mPerson.Name, Changed = mPerson.Changed };

答案 3 :(得分:1)

spender比我快,我没有任何扩展方法。

没有任何扩展方法:

List<Person> mergedList =  leftList
            .GroupJoin(
                rightList, left => left.Id, right => right.Id, 
                (x, y) => new { Left = x, Rights = y  }
            )
            .SelectMany(
                x => x.Rights.DefaultIfEmpty(),
                (x, y) => new Person
                {
                    Id = x.Left.Id, 
                    Name = x.Left.Name, 
                    Changed = y == null ? x.Left.Changed : y.Changed
                }
            ).ToList();

GroupJoin进行左外连接操作。