如何在LinQ中合并多个集合

我在Linq有3套,像这样:

struct Index { string code; int indexValue; } List reviews List products List pages 

这些列表有不同的代码。

我想合并这些集如下:

  • 在评论中取第一个
  • 拿第一个产品
  • 以第一页为准
  • 在评论-…中取第二个,依此类推,请注意这些列表的大小不同。

我怎么能在Linq做到这一点?

编辑:等等,如果没有.NET 4.0,是否有更改?

非常感谢你

您可以使用Zip来进行出价。

 var trios = reviews .Zip(products, (r, p) => new { Review = r, Product = p }) .Zip(pages, (rp, p) => new { rp.Review, rp.Product, Page = p }); 

编辑:

对于.NET 3.5,可以很容易地实现Zip :但是有一些问题 。 Jon Skeet有一篇关于如何实现LINQ到对象运算符(用于教育目的)的精彩系列文章,包括Zip上的这篇文章 。 整个系列的源代码edulinq可以在Google Code上找到 。

简单的答案

要将它们合并到一个没有任何公共数据的公共列表中,使用它们出现的顺序,您可以使用Zip方法 :

 var rows = reviews .Zip(products, (r, p) => new { Review = r, Product = p }) .Zip(pages, (rp, page) => new { rp.Review, rp.Product, Page = page }); 

此解决方案的问题是列表的长度必须相同,否则您的结果将被切割为原始列表的最短列表。

编辑:

如果您不能使用.Net 4,请查看Jon Skeet关于Linq的洁净室实施的博客文章 ,特别是关于Zip的文章 。

如果你正在使用.Net 2,那么试试他的库(可能)或尝试LinqBridge

如何处理不同长度的列表

您可以将列表预填充到所需的长度。 我找不到现有方法来执行此操作,因此我使用扩展方法:

 public static class EnumerableExtensions { public static IEnumerable Pad(this IEnumerable source, int desiredCount, T padWith = default(T)) { // Note: Not using source.Count() to avoid double-enumeration int counter = 0; var enumerator = source.GetEnumerator(); while(counter < desiredCount) { yield return enumerator.MoveNext() ? enumerator.Current : padWith; ++counter; } } } 

你可以像这样使用它:

 var paddedReviews = reviews.Pad(desiredLength); var paddedProducts = products.Pad(desiredLength, new Product { Value2 = DateTime.Now } ); 

完整的编译样本和相应的输出

 using System; using System.Collections.Generic; using System.Linq; class Review { public string Value1; } class Product { public DateTime Value2; } class Page { public int Value3; } public static class EnumerableExtensions { public static IEnumerable Pad(this IEnumerable source, int desiredCount, T padWith = default(T)) { int counter = 0; var enumerator = source.GetEnumerator(); while(counter < desiredCount) { yield return enumerator.MoveNext() ? enumerator.Current : padWith; ++counter; } } } class Program { static void Main(string[] args) { var reviews = new List { new Review { Value1 = "123" }, new Review { Value1 = "456" }, new Review { Value1 = "789" }, }; var products = new List() { new Product { Value2 = DateTime.Now }, new Product { Value2 = DateTime.Now.Subtract(TimeSpan.FromSeconds(5)) }, }; var pages = new List() { new Page { Value3 = 123 }, }; int maxCount = Math.Max(Math.Max(reviews.Count, products.Count), pages.Count); var rows = reviews.Pad(maxCount) .Zip(products.Pad(maxCount), (r, p) => new { Review = r, Product = p }) .Zip(pages.Pad(maxCount), (rp, page) => new { rp.Review, rp.Product, Page = page }); foreach (var row in rows) { Console.WriteLine("{0} - {1} - {2}" , row.Review != null ? row.Review.Value1 : "(null)" , row.Product != null ? row.Product.Value2.ToString() : "(null)" , row.Page != null ? row.Page.Value3.ToString() : "(null)" ); } } } 

123 - 9/7/2011 10:02:22 PM - 123
456 - 9/7/2011 10:02:17 PM - (null)
789 - (null) - (null)

使用Join标签

此操作不是逻辑Join 。 这是因为您匹配索引,而不是每个对象的任何数据。 每个对象都必须有一个共同的其他数据(除了它们在列表中的位置),你可以在关系数据库中找到一个Join的意义。