Как объединить более двух общих списков в С# 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"}
}
Как это сделать?
Ответы
Ответ 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