2009-12-18 117 views
1

我有2個列表,我需要合併來自A和B的連接值,但也包括來自A和B的與連接不匹配的值。Linq查詢合併2個列表

class TypeA 
{ 
    public string Key { get; set; } 
    public int ValueA { get; set; } 
} 

class TypeB 
{ 
    public string Key { get; set; } 
    public int ValueB { get; set; } 
} 

class TypeAB 
{ 
    public string Key { get; set; } 
    public int ValueA { get; set; } 
    public int ValueB { get; set; } 
} 

var listA = new List<TypeA> 
{ 
    new TypeA { Key = "one", Value = 1 }, 
    new TypeA { Key = "two", Value = 2 }, 
}; 

var listB = new List<TypeB> 
{ 
    new TypeB { Key = "two", Value = 2 }, 
    new TypeB { Key = "three", Value = 3 }, 
}; 

我想這些列表合併等於這個:

var listAB = new List<TypeAB> 
{ 
    new TypeAB { Key = "one", ValueA = 1, ValueB = null }, 
    new TypeAB { Key = "two", ValueA = 2, ValueB = 2 }, 
    new TypeAB { Key = "three", ValueA = null, ValueB = 3 }, 
}; 

什麼是Linq的語句,將做到這一點?我一直在玩,不能完全到達那裏。我可以通過在A到B和Union上進行左外連接來獲得差不多的空間,但是我會得到重複的相交值。

更新

這裏是我根據喬治的回答是什麼:

var joined = 
    (from a in listA 
     join b in listB 
     on a.Key equals b.Key 
     into listBJoin 
     from b in listBJoin.DefaultIfEmpty(new TypeB()) 
     select new TypeAB 
     { 
     Key = a.Key, 
     ValueA = a.ValueA, 
     ValueB = b.ValueB, 
     }).Union(
     from b in listB 
     where !listA.Any(d => d.Key == b.Key) 
     select new TypeAB 
     { 
      Key = b.Key, 
      ValueB = b.ValueB, 
     } 
     ).ToList(); 
+0

發佈另一個略微更有趣的響應 – 2009-12-18 20:27:40

+0

一些問題/答案,只是沒有得到他們應得的upvotes。這兩個答案都應該更好。他們至少得到了我的讚揚。 – 2010-03-17 17:49:37

回答

1

編輯:搞掂。這是馬虎,但它應該工作。

listA //Start with lisA 
.Where(a=>!listB.Any(b=>a.Key == b.Key)) // Remove any that are duplicated in listB 
.Select(a => new TypeAB() { Key=a.Key, ValueA=a.Value}) // Map each A to an AB 
.Union(
    listB.Select(b => { 
     var correspondingA = listA.FirstOrDefault(a => a.Key == b.Key); //Is there an a that corresponds? 
    return new TypeAB() { Key=b.Key, 
     ValueB=b.Value, //Value B is easy 
     ValueA= correspondingA!=null ? (int?)correspondingA.Value : null //If there is an A than map its value 
    }; 
    }) 
) 

順便說一句,如果你使用這個作爲某種域操作,類型A和類型B的可能應該基於某種AorBIsAConceptThatHasMeaningInTheDomain基類。只要您發現自己將列表結合起來,這只是一個通用規則。如果不存在這樣的概念,那麼你可能不需要組合這些列表。另一方面,如果您將這作爲映射的一部分(例如將域對象映射到UI),則可以使用匿名類型而不是TypeAB類來簡化代碼。 (或許不是,這隻能靠個人喜好)

編輯編輯 下面是使用哈希

 var listAB = listA.Cast<object>().Union(listB.Cast<object>()).ToLookup(x => x is TypeA ? (x as TypeA).Key : (x as TypeB).Key) 
       .Select(kv => { 
        var a = kv.FirstOrDefault(x => x is TypeA) as TypeA; 
        var b = kv.FirstOrDefault(x => x is TypeB) as TypeB; 
        return new TypeAB() { 
         Key = kv.Key, 
         ValueA = a != null ? (int?)a.Value : null, 
         ValueB = b != null ? (int?)b.Value : null 
        }; 
       }).ToList(); 
+0

這將產生不正確的結果,例如鍵==「two」。實際上,它會有ValueA == null。 – 2009-12-18 19:51:27

+0

好點,錯過了Req,嗯,如果在listA中有多個TypeA具有相同的鍵,會發生什麼? – 2009-12-18 19:58:01

+0

你的回答給了我一個想法,在從listA到listB的LEFT OUTER JOIN之後不包括已經包含在listB中的結果。 – 2009-12-18 20:05:01

3

正是這種情況下,在我們的項目中,我們使用延長稍微理智有趣的答案稱爲Merge的方法。

public static class Extensions 
{ 
    public static IEnumerable<TResult> Merge<TLeft, TRight, TKey, TResult>(
     this IEnumerable<TLeft> leftList, 
     IEnumerable<TRight> rightList, 
     Func<TLeft, TKey> leftKeySelector, 
     Func<TRight, TKey> rightKeySelector, 
     Func<TKey, IEnumerable<TLeft>, IEnumerable<TRight>, TResult> combiner) 
    { 
     var leftLookup = leftList.ToLookup(leftKeySelector); 
     var rightLookup = rightList.ToLookup(rightKeySelector); 

     var keys = leftLookup.Select(g => g.Key).Concat(rightLookup.Select(g => g.Key)).Distinct(); 
     return keys.Select(key => combiner(key, leftLookup[key], rightLookup[key]));   
    } 
} 

您可以使用合併這樣

var listAB = listA.Merge(
    listB, 
    a => a.Key, 
    b => b.Key, 
    (key, aItems, bItems) => new TypeAB 
    { 
     Key = key, 
     ValueA = aItems.Select(a => (int?)a.Value).SingleOrDefault(), 
     ValueB = bItems.Select(b => (int?)b.Value).SingleOrDefault() 
    }); 
+0

看起來很熟悉;-) – jeroenh 2009-12-19 21:10:36