我有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();
答案 0 :(得分: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()
});
答案 1 :(得分: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
};
})
)
顺便说一句,如果您将此作为某种域操作使用,则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();