如何在LinQ中合并多个集合

时间:2011-09-08 04:12:23

标签: c# linq merge

我在Linq有3套,像这样:

struct Index 
{
    string code;
    int indexValue;
}

List<Index> reviews
List<Index> products
List<Index> pages

这些列表有不同的代码。

我想将这些集合合并如下:

  • 在评论中取得第一个
  • 采取第一个产品
  • 采取第一页
  • 在评论中选择第二名 -...等等,请注意这些列表的大小不同。

我如何在Linq中执行此操作?

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

非常感谢

2 个答案:

答案 0 :(得分:4)

您可以使用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:但是有一些 gotcha 。 Jon Skeet有一篇关于如何实现LINQ到对象运算符(用于教育目的)的精彩系列文章,包括this post, on Zip。整个系列的源代码edulinq可以是found on Google Code

答案 1 :(得分:1)

简单的答案

要将它们合并到一个没有任何常见数据的公共列表中,使用它们显示的顺序,您可以use the Zip method

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's blog posts on a clean-room implementation of LinqHis article on Zip in particular

如果您使用的是.Net 2,请尝试使用他的库(可能)或try LinqBridge

如何处理不同长度的列表

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

public static class EnumerableExtensions
{
    public static IEnumerable<T> Pad<T>(this IEnumerable<T> 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<T> Pad<T>(this IEnumerable<T> 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<Review>
        {
            new Review { Value1 = "123" },
            new Review { Value1 = "456" },
            new Review { Value1 = "789" },
        };
        var products = new List<Product>()
        {
            new Product { Value2 = DateTime.Now },
            new Product { Value2 = DateTime.Now.Subtract(TimeSpan.FromSeconds(5)) },
        };
        var pages = new List<Page>()
        {
            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的意义。