简体   繁体   English

LINQ合并两个列表(复合键上的完整外部联接)

[英]LINQ merging two lists(full outer join on composite keys)

I have two lists 我有两个清单

 IEnumerable<Citrus> grapefruit = citrusList.Where(x => x.IsSmall == false);
 IEnumerable<Citrus> tangerines = citrusList.Where(x => x.IsSmall == true);

I want to put my all of Citrus in a PackingContainer, but I want to first make tangelos-- a combination of grapefruit and tangerine-- from my grapefruits and tangerines where the Citrus.Color = orange, Citrus.flavor = very tangy, Citrus.Texture = grainy and the Citrus.State = ripe 我想将所有柑橘类水果放入一个PackingContainer中,但首先要从柚子和橘子中制作橘子-柚子和橘子的混合物-橘子=橘子,柑橘-风味=非常浓郁,柑橘纹理=颗粒状和柑橘状态=成熟

Right now I have nested foreach loops that check 现在我嵌套了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);
           }
     }
  }

But I'm sure there's a better way to do this. 但我敢肯定,有更好的方法可以做到这一点。 I want to essentially do a full outer join (union all grapefruit and tangerines, but make tangelos out of the intersection). 我本质上想做一个完整的外部连接(将所有葡萄柚和橘子结合在一起,但使橘子不在交点内)。 My end goal is to have a PackingContainer that has some grapefruit, some tangerines, and some tangelos in it. 我的最终目标是要拥有一个包含一些葡萄柚,一些橘子和一些橘子的PackingContainer。 I'm sure there's a more elegant way to do that in LINQ. 我敢肯定,LINQ中有一种更优雅的方法可以做到这一点。

...but I can't figure it out from http://msdn.microsoft.com/en-us/library/bb907099.aspx and http://msdn.microsoft.com/en-us/library/bb384063.aspx and it's not exactly a Union because I'm modifying intersecting members (http://msdn.microsoft.com/en-us/library/bb341731.aspx) ...但是我无法从http://msdn.microsoft.com/en-us/library/bb907099.aspxhttp://msdn.microsoft.com/en-us/library/bb384063中弄清楚。 aspx ,它不完全是联盟,因为我正在修改相交的成员(http://msdn.microsoft.com/zh-cn/library/bb341731.aspx)

Little help? 没有什么帮助?

It actually sounds like you need an inner join, not an outer. 实际上,听起来您需要一个内部联接,而不是一个外部联接。 Your nested for loops are actually performing the equivalent of an inner join. 嵌套的for循环实际上执行的是内部联接。 At any rate: 好歹:

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));

Where 'Map' is a 'ForEach'-esque extension method for IEnumerables: 其中“地图”是IEnumerables的“ ForEach”式扩展方法:

public static void Map<T>(this IEnumerable<T> source, Action<T> func)
{
    foreach (T i in source)
        func(i);
}

EDIT: Fair enough. 编辑:足够公平。 From the question it sounded like you were only interested in the tangelos. 从这个问题看来,您似乎只对橘子感兴趣。 Here is an Outer Join version (This is untested, so let me know if anything doesn't work!): 这是外部联接版本(未经测试,所以如果有任何问题,请通知我!):

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
   )
  );

And then you can add them to the container using the map method or the AddRange method from David B's answer. 然后可以使用David B的答案中的map方法或AddRange方法将它们添加到容器中。

You don't want a full outer join for that, or you'd wind up making some tangelos without Grapefruit and some tangelos without Tangerines. 您不希望为此而加入一个完整的外部连接,或者您会结出一些没有葡萄柚的橘子和一些没有橘子的橘子。

here's an inner join. 这是一个内部联接。

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()

Even that is suspect. 即使那样也令人怀疑。 What if 3 Grapefruit match 1 Tangerine, then you get 3 Tangelos! 如果3个葡萄柚与1个橘子匹配,那么您会得到3个橘子!

Try this filtering to get only one Tangelo per tangerine: 尝试进行以下过滤,以使每个橘子只得到一个橘子:

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()

Of course, once you have a List of Tangelos, you can pack them by 当然,一旦有了“橘子清单”,就可以将它们打包

container.AddRange(tangelos);

I think this does the trick: 我认为这可以解决问题:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM