問題:
如何基於公共鍵字段在兩個對象列表之間執行全外連接,確保兩個列表中的所有記錄都包含在結果中,即使它們在另一個列表中沒有對應的匹配項?
答案:
為了在 LINQ 中實現全外連接,我們可以定義一個自定義擴展方法,如下所示:
<code class="language-csharp">public static IEnumerable<TResult> FullOuterJoin<TA, TB, TKey, TResult>( this IEnumerable<TA> a, IEnumerable<TB> b, Func<TA, TKey> selectKeyA, Func<TB, TKey> selectKeyB, Func<TA, TB, TKey, TResult> projection, TA defaultA = default(TA), TB defaultB = default(TB), IEqualityComparer<TKey> cmp = null) { cmp = cmp ?? EqualityComparer<TKey>.Default; var alookup = a.ToLookup(selectKeyA, cmp); var blookup = b.ToLookup(selectKeyB, cmp); var keys = new HashSet<TKey>(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; }</code>
在提供的示例中,我們有兩個列表:firstNames
和 lastNames
。要執行全外連接,我們可以使用以下代碼:
<code class="language-csharp">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 };</code>
此代碼使用左外連接,其中 firstNames
中的每條記錄都與 lastNames
中的第一條匹配記錄連接,如果找不到匹配項,則連接到默認值。 into temp
語句創建了一個 lastNames
中匹配記錄的臨時集合,允許我們應用 DefaultIfEmpty()
以確保包含不匹配的記錄。 這實際上實現了左外連接,並非完整意義上的全外連接。 要實現全外連接,需要使用上面自定義的FullOuterJoin
擴展方法。
全外連接的預期輸出為:
<code>ID FirstName LastName -- --------- -------- 1 John Doe 2 Sue 3 Smith</code>
提供的擴展方法允許您為不匹配的記錄指定默認值。在示例中,我們為不匹配的名稱指定了 string.Empty
,為不匹配的 ID 指定了負整數。
全外連接的運行時間為 O(n m),其中 n 和 m 是兩個輸入列表的長度。這是因為 ToLookup()
操作需要 O(n) 時間,後續操作需要 O(n m) 時間。
全外連接的這種實現目前不是 LINQ 標準的一部分,但已建議將其包含在未來的版本中。 標準LINQ不直接支持全外連接,需要自定義擴展方法來實現。
以上是如何在LINQ中執行完整的外部連接?的詳細內容。更多資訊請關注PHP中文網其他相關文章!