[英]LINQ merging two lists(full outer join on composite keys)
我有两个清单
IEnumerable<Citrus> grapefruit = citrusList.Where(x => x.IsSmall == false);
IEnumerable<Citrus> tangerines = citrusList.Where(x => x.IsSmall == true);
我想将所有柑橘类水果放入一个PackingContainer中,但首先要从柚子和橘子中制作橘子-柚子和橘子的混合物-橘子=橘子,柑橘-风味=非常浓郁,柑橘纹理=颗粒状和柑橘状态=成熟
现在我嵌套了foreach循环来检查
foreach (Citrus fruit in grapefruit)
{
foreach (Citrus fruitToo in tangerines)
{
PackingContainer container = new PackingContainer();
if (fruit.Color == fruitToo.Color &&
fruit.Flavor == fruitToo.Flavor &&
fruit.Texture == fruitToo.Texture &&
fruit.State == fruitToo.State)
{
Tangelo tangy = new Tangelo(fruit.Color, fruit.Flavor, fruit.Texture, fruit.State, "A tangelo", new Decimal(0.75);
container.Add(tangy);
}
}
}
但我敢肯定,有更好的方法可以做到这一点。 我本质上想做一个完整的外部连接(将所有葡萄柚和橘子结合在一起,但使橘子不在交点内)。 我的最终目标是要拥有一个包含一些葡萄柚,一些橘子和一些橘子的PackingContainer。 我敢肯定,LINQ中有一种更优雅的方法可以做到这一点。
...但是我无法从http://msdn.microsoft.com/en-us/library/bb907099.aspx和http://msdn.microsoft.com/en-us/library/bb384063中弄清楚。 aspx ,它不完全是联盟,因为我正在修改相交的成员(http://msdn.microsoft.com/zh-cn/library/bb341731.aspx)
没有什么帮助?
实际上,听起来您需要一个内部联接,而不是一个外部联接。 嵌套的for循环实际上执行的是内部联接。 好歹:
grapefruit
.Join(
tangerines,
x => new { Color = x.Color, Flavor = x.Flavor, Texture = x.Texture, State = x.State },
x => new { Color = x.Color, Flavor = x.Flavor, Texture = x.Texture, State = x.State },
(o,i) => new Tangelo(o.Color, o.Flavor, o.Texture, o.State, "A tangelo", new Decimal(0.75))
).Map(x => container.Add(x));
其中“地图”是IEnumerables的“ ForEach”式扩展方法:
public static void Map<T>(this IEnumerable<T> source, Action<T> func)
{
foreach (T i in source)
func(i);
}
编辑:足够公平。 从这个问题看来,您似乎只对橘子感兴趣。 这是外部联接版本(未经测试,所以如果有任何问题,请通知我!):
var q =
from fruit in grapefruit.Select(x => new { x.Color, x.Flavor, x.Texture, x.State })
.Union(tangerines.Select(x => new { x.Color, x.Flavor, x.Texture, x.State }))
join g in grapefruit on fruit equals new { g.Color, g.Flavor, g.Texture, g.State } into jg
from g in jg.DefaultIfEmpty()
join t in tangerines on fruit equals new { t.Color, t.Flavor, t.Texture, t.State } into jt
from t in jt.DefaultIfEmpty()
select (g == null ?
t as Citrus :
(t == null ?
g as Citrus :
new Tangelo(g.Color, g.Flavor, g.Texture, g.State, "A tangelo", new Decimal(0.75)) as Citrus
)
);
然后可以使用David B的答案中的map方法或AddRange方法将它们添加到容器中。
您不希望为此而加入一个完整的外部连接,或者您会结出一些没有葡萄柚的橘子和一些没有橘子的橘子。
这是一个内部联接。
List<Tangelo> tangelos = (
from fruit in grapefruit
join fruitToo in tangerines
on new {fruit.Flavor, fruit.Color, fruit.Flavor, fruit.State}
equals new {fruitToo.Flavor, fruitToo.Color, fruitToo.Flavor, fruitToo.State}
select new Tangelo(fruit.Color, fruit.Flavor, fruit.Texture, fruit.State,
"A tangelo", new Decimal(0.75))
).ToList()
即使那样也令人怀疑。 如果3个葡萄柚与1个橘子匹配,那么您会得到3个橘子!
尝试进行以下过滤,以使每个橘子只得到一个橘子:
List<Tangelo> tangelos = (
from fruit in tangerines
where grapefruit.Any(fruitToo =>
new {fruit.Flavor, fruit.Color, fruit.Flavor, fruit.State}
== new {fruitToo.Flavor, fruitToo.Color, fruitToo.Flavor, fruitToo.State})
select new Tangelo(fruit.Color, fruit.Flavor, fruit.Texture, fruit.State,
"A tangelo", new Decimal(0.75))
).ToList()
当然,一旦有了“橘子清单”,就可以将它们打包
container.AddRange(tangelos);
我认为这可以解决问题:
var cs = from c in citrusList
group c by new { c.Color, c.Flavor, c.Texture, c.State } into gcs
let gs = gcs.Where(gc => gc.IsSmall == false)
let ts = gcs.Where(gc => gc.IsSmall == true)
let Tangelos = gs
.Zip(ts, (g, t) =>
new Tangelo(g.Color, g.Flavor, g.Texture, g.State,
"A tangelo", new Decimal(0.75)))
select new
{
gcs.Key,
Grapefruit = gs.Skip(Tangelos.Count()),
Tangerines = ts.Skip(Tangelos.Count()),
Tangelos,
};
var container = new PackingContainer();
container.AddRange(from c in cs
from f in c.Grapefruit
.Concat(c.Tangerines)
.Concat(c.Tangelos.Cast<Citrus>())
select f);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.