左连接两个列表并使用 Linq 从右侧维护一个属性
Posted
技术标签:
【中文标题】左连接两个列表并使用 Linq 从右侧维护一个属性【英文标题】:Left join on two Lists and maintain one property from the right with Linq 【发布时间】:2016-03-01 21:26:59 【问题描述】:我有 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 做到这一点?谢谢。
【问题讨论】:
【参考方案1】:这看起来像是一个非常标准的左外连接场景。
我总是将此扩展方法方便地用于左外连接,因此我不必查找如何使用讨厌的查询语法(或记住 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 PersonId = lft.Id,
Name = lft.Name,
Changed = rgt == null ? lft.Changed : rgt.Changed)
【讨论】:
你会在最后一行得到一个 nullref。更改后应分配rgt == null ? lft.Changed : rgt.Changed
@CameronMacFarland:Ta!
您好,感谢您的帮助,抱歉耽搁了,我在这里很忙。不错的扩展,谢谢分享!
@RenanAraújo 哈哈!没关系。 Resharper 几乎是为我写的!【参考方案2】:
为什么不试试这样的解决方案:
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();
【讨论】:
【参考方案3】: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 进行左外连接操作。
【讨论】:
【参考方案4】:另一种方法是:
//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 ;
【讨论】:
以上是关于左连接两个列表并使用 Linq 从右侧维护一个属性的主要内容,如果未能解决你的问题,请参考以下文章