[英]Linq Query To Combine 2 Lists
我有2個列表,我需要組合A和B的連接值,但也包括A和B中與連接不匹配的值。
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上的左外連接來到達那里,在B到A的左外連接,但我得到重復的交點值。
更新
以下是我根據喬治的回答所做的事情:
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();
對於這種情況,在我們的項目中,我們使用名為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]));
}
}
您可以像這樣使用Merge
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()
});
編輯:修正了。 這很草率但應該有效。
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
};
})
)
順便說一句,如果你將它用作某種域操作,TypeA和TypeB可能應該基於某種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();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.