問題:
如何基於公共鍵字段在兩個對象列表之間執行全外連接,確保兩個列表中的所有記錄都包含在結果中,即使它們在另一個列表中沒有對應的匹配項?
答案:
為了在 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