Как объединить несколько наборов в LinQ - PullRequest
1 голос
/ 08 сентября 2011

У меня есть 3 сета в Linq, вот так:

struct Index 
{
    string code;
    int indexValue;
}

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

Эти списки имеют другой код.

Я хочу объединить эти наборы следующим образом:

  • Примите первое в обзорах
  • Возьмите первый в продуктах
  • Возьмите первое на страницах
  • возьмите второе в обзорах -... и так далее, обратите внимание, что эти списки не одинакового размера.

Как я могу сделать это в Linq?

РЕДАКТИРОВАТЬ: Подождите, есть ли изменения, чтобы сделать это без .NET 4.0?

Большое спасибо

Ответы [ 2 ]

4 голосов
/ 08 сентября 2011

Вы можете использовать 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 s.У Джона Скита есть замечательная серия статей о том, как реализовать LINQ для операторов объектов (в образовательных целях), включая этот пост на Zip.Исходный код всей серии edulinq может быть найден в Google Code .

1 голос
/ 08 сентября 2011

Простой ответ

Чтобы объединить их в общий список без каких-либо общих данных, используя порядок их отображения, вы можете использовать метод 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, ознакомьтесь с сообщениями в блоге Джона Скита о реализации Linq и в чистой комнате, в частности его статьи о Zip .

Если вы используете .Net 2, то попробуйте его библиотеку (возможно) или попробуйте 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.09.2011 22:02:22 - 123
456 - 9.09.2011 22:02:17 - (ноль)
789 - (ноль) - (ноль)

При использовании тега Join

Эта операция не логична Join. Это потому, что вы сопоставляете по индексу, а не по каким-либо данным из каждого объекта. Каждый объект должен иметь другие общие данные (помимо их позиции в списках) для объединения в смысле Join, который вы найдете в реляционной базе данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...