代码之家  ›  专栏  ›  技术社区  ›  Visionary Software Solutions

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

  •  0
  • Visionary Software Solutions  · 技术社区  · 14 年前

    我有两张单子

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

    现在我已经嵌套了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);
               }
         }
      }
    

    但我相信有更好的办法。我基本上想做一个完全的外部连接(将所有的葡萄柚和橘子联合起来,但要使橘子离开交叉点)。我的最终目标是有一个包装容器,里面有一些葡萄柚,一些橘子,和一些橘子。我相信在林肯有一个更优雅的方法。

    ……但我无法从 http://msdn.microsoft.com/en-us/library/bb907099.aspx http://msdn.microsoft.com/en-us/library/bb384063.aspx

    没什么帮助?

    3 回复  |  直到 14 年前
        1
  •  0
  •   Amy B    14 年前

    你不想要一个完整的外部连接,否则你会做一些没有葡萄柚的橘子和一些没有橘子的橘子。

    这是一个内部连接。

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

    尝试此筛选以每个橘子仅获取一个橘子:

    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);
    
        2
  •  0
  •   diceguyd30    14 年前

    实际上听起来你需要一个内部连接,而不是外部连接。嵌套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));
    

    其中“Map”是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方法将它们添加到容器中。

        3
  •  0
  •   Enigmativity    14 年前

    我想这就是诀窍:

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