Подтвердить что ты не робот

Как объединить более двух общих списков в С# Zip?

У меня есть три (возможно иметь более 3-4 общих списков, но в этом примере пусть 3) общие списки.

List<string> list1

List<string> list2

List<string> list3

все списки имеют одинаковое количество элементов (одинаковые значения).

Я использовал это для объединения двух списков с ZIP:

var result = list1.Zip(list2, (a, b) => new {
  test1 = f,
  test2 = b
}

Я использовал это для оператора foreach, чтобы избежать foreach каждого списка, например

foreach(var item in result){
Console.WriteLine(item.test1 + " " + item.test2);
}

Как использовать simmilary с Zip для трех списков?

Спасибо

EDIT:

Я хочу:

List<string> list1 = new List<string>{"test", "otherTest"};

List<string> list2 = new List<string>{"item", "otherItem"};

List<string> list3 = new List<string>{"value", "otherValue"};

после ZIP (я не знаю метода), я хочу привести (в режиме отладки VS2010)

[0] { a = {"test"},
      b = {"item"},
      c = {"value"}
    }   

[1] { a = {"otherTest"},
      b = {"otherItem"},
      c = {"otherValue"}
    }  

Как это сделать?

4b9b3361

Ответ 1

Наиболее очевидным способом для меня было бы использовать Zip дважды.

Например,

var results = l1.Zip(l2, (x, y) => x + y).Zip(l3, (x, y) => x + y);

будет объединять (добавлять) элементы из трех объектов List<int>.

Update:

Вы можете определить новый метод расширения, который действует как Zip с тремя IEnumerable s, например:

public static class MyFunkyExtensions
{
    public static IEnumerable<TResult> ZipThree<T1, T2, T3, TResult>(
        this IEnumerable<T1> source,
        IEnumerable<T2> second,
        IEnumerable<T3> third,
        Func<T1, T2, T3, TResult> func)
    {
        using (var e1 = source.GetEnumerator())
        using (var e2 = second.GetEnumerator())
        using (var e3 = third.GetEnumerator())
        {
            while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
                yield return func(e1.Current, e2.Current, e3.Current);
        }
    }
}

Теперь использование (в том же контексте, что и выше):

var results = l1.ZipThree(l2, l3, (x, y, z) => x + y + z);

Аналогично, теперь три списка можно комбинировать с:

var results = list1.ZipThree(list2, list3, (a, b, c) => new { a, b, c });

Ответ 2

Есть еще одно довольно интересное решение, о котором я знаю. Это интересно в основном с образовательной точки зрения, но если нужно выполнить zipping разное количество списков LOT, то это также может быть полезно.

Этот метод переопределяет функцию .NET LINQ SelectMany, которая берется по соглашению при использовании синтаксиса запроса LINQ. Стандартная реализация SelectMany выполняет декартово произведение. Вместо этого можно сделать замену. Фактическая реализация может быть:

static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source,
        Func<TSource, IEnumerable<TCollection>> selector, Func<TSource, TCollection, TResult> select)
{
    using (var e1 = source.GetEnumerator())
        using (var e2 = selector(default(TSource)).GetEnumerator())
            while (true)
                if (e1.MoveNext() && e2.MoveNext())
                    yield return select(e1.Current, e2.Current);
                else
                    yield break;
}

Это выглядит немного страшно, но это логика zipping, которая, если она написана один раз, может использоваться во многих местах, а код клиента выглядит довольно красиво - вы можете закрепить любое число IEnumerable<T> с помощью стандартного синтаксиса запроса LINQ:/p >

var titles = new string[] { "Analyst", "Consultant", "Supervisor"};
var names = new string[] { "Adam", "Eve", "Michelle" };
var surnames = new string[] { "First", "Second", "Third" };

var results =
    from title in titles
    from name in names
    from surname in surnames
    select $"{ title } { name } { surname }";

Если вы затем выполните:

foreach (var result in results)
    Console.WriteLine(result);

Вы получите:

Analyst Adam First
Consultant Eve Second
Supervisor Michelle Third

Вы должны сохранить это расширение частным в своем классе, потому что иначе вы радикально измените поведение окружающего кода. Кроме того, новый тип будет полезен, так что он не будет смешиваться со стандартным поведением LINQ для IEnumerables.

В образовательных целях я создал небольшой проект С# с этим методом расширения + несколько преимуществ: https://github.com/lukiasz/Zippable

Кроме того, если вы найдете это интересным, я настоятельно рекомендую Jon Skeet Reimplementing LINQ to Objects articles.

Удачи!

Ответ 3

Вы можете комбинировать множество списков в С# с каскадными zip-методами и анонимными классами и результатом Tuple.

List<string> list1 = new List<string> { "test", "otherTest" };
List<string> list2 = new List<string> { "item", "otherItem" };
List<string> list3 = new List<string> { "value", "otherValue" };

IEnumerable<Tuple<string, string, string>> result = list1
    .Zip(list2, (e1, e2) => new {e1, e2})
    .Zip(list3, (z1, e3) => Tuple.Create(z1.e1, z1.e2, e3));

Результат:

[0]
{(test, item, value)}
    Item1: "test"
    Item2: "item"
    Item3: "value"

Ответ 4

class Program
{
    static void Main(string[] args)
    {
        List<string> list1 = new List<string> { "test", "otherTest" };
        List<string> list2 = new List<string> { "item", "otherItem" };
        List<string> list3 = new List<string> { "value", "otherValue" };

        var result = CombineListsByLayers(list1, list2, list3);
    }

    public static List<string>[] CombineListsByLayers(params List<string>[] sourceLists)
    {
        var results = new List<string>[sourceLists[0].Count];

        for (var i = 0; i < results.Length; i++)
        {
            results[i] = new List<string>();
            foreach (var sourceList in sourceLists)
                results[i].Add(sourceList[i]);
        }
        return results;
    }

Ответ 5

Вы можете объединить эти List<string> в List<List<string>> и объединить их

List<string> list1 = new List<string> { "test", "otherTest" };
List<string> list2 = new List<string> { "item", "otherItem" };
List<string> list3 = new List<string> { "value", "otherValue" };

var list = new List<List<string>>() { list1, list2, list3 }
    .Aggregate(
        Enumerable.Range(0, list1.Count).Select(e => new List<string>()),
        (prev, next) => prev.Zip(next, (first, second) => { first.Add(second); return first; })
    )
    .Select(e => new
    {
        a = e.ElementAt(0),
        b = e.ElementAt(1),
        c = e.ElementAt(2)
    });

Результат

[
  {
    "a": "test",
    "b": "item",
    "c": "value"
  },
  {
    "a": "otherTest",
    "b": "otherItem",
    "c": "otherValue"
  }
]

Смотрите на dotnetfiddle.net