问题:
如何基于公共键字段在两个对象列表之间执行全外连接,确保两个列表中的所有记录都包含在结果中,即使它们在另一个列表中没有对应的匹配项?
答案:
为了在 LINQ 中实现全外连接,我们可以定义一个自定义扩展方法,如下所示:
public static IEnumerable FullOuterJoin(
this IEnumerable a,
IEnumerable b,
Func selectKeyA,
Func selectKeyB,
Func projection,
TA defaultA = default(TA),
TB defaultB = default(TB),
IEqualityComparer cmp = null)
{
cmp = cmp ?? EqualityComparer.Default;
var alookup = a.ToLookup(selectKeyA, cmp);
var blookup = b.ToLookup(selectKeyB, cmp);
var keys = new HashSet(alookup.Select(p => p.Key), cmp);
keys.UnionWith(blookup.Select(p => p.Key));
var join = from key in keys
from xa in alookup[key].DefaultIfEmpty(defaultA)
from xb in blookup[key].DefaultIfEmpty(defaultB)
select projection(xa, xb, key);
return join;
}
在提供的示例中,我们有两个列表:firstNames
和 lastNames
。要执行全外连接,我们可以使用以下代码:
var outerJoin = from first in firstNames
join last in lastNames
on first.ID equals last.ID
into temp
from last in temp.DefaultIfEmpty()
select new
{
id = first != null ? first.ID : last.ID,
firstname = first != null ? first.Name : string.Empty,
surname = last != null ? last.Name : string.Empty
};
此代码使用左外连接,其中 firstNames
中的每条记录都与 lastNames
中的第一条匹配记录连接,如果找不到匹配项,则连接到默认值。into temp
语句创建了一个 lastNames
中匹配记录的临时集合,允许我们应用 DefaultIfEmpty()
以确保包含不匹配的记录。 这实际上实现了左外连接,并非完整意义上的全外连接。 要实现全外连接,需要使用上面自定义的FullOuterJoin
扩展方法。
全外连接的预期输出为:
ID FirstName LastName
-- --------- --------
1 John Doe
2 Sue
3 Smith
提供的扩展方法允许您为不匹配的记录指定默认值。在示例中,我们为不匹配的名称指定了 string.Empty
,为不匹配的 ID 指定了负整数。
全外连接的运行时间为 O(n m),其中 n 和 m 是两个输入列表的长度。这是因为 ToLookup()
操作需要 O(n) 时间,后续操作需要 O(n m) 时间。
全外连接的这种实现目前不是 LINQ 标准的一部分,但已建议将其包含在未来的版本中。 标准LINQ不直接支持全外连接,需要自定义扩展方法来实现。
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3