If I have a list containing an arbitrary number of lists, like so:
var myList = new List>()
{
new List() { \"a\",
(from count in Range(myList[0].Count)
select new List<string>(
from count2 in Range(myList.Count)
select myList[count2][count])
).ToList();
It ain't pretty, but I think it'll work.
You can condense for
loops using Range:
var result = Enumerable.Range(0, myList.Min(l => l.Count))
.Select(i => myList.Select(l => l[i]).ToList()).ToList();
How about using SelectMany
and GroupBy
with some indexes?
// 1. Project inner lists to a single list (SelectMany)
// 2. Use "GroupBy" to aggregate the item's based on order in the lists
// 3. Strip away any ordering key in the final answer
var query = myList.SelectMany(
xl => xl.Select((vv,ii) => new { Idx = ii, Value = vv }))
.GroupBy(xx => xx.Idx)
.OrderBy(gg => gg.Key)
.Select(gg => gg.Select(xx => xx.Value));
From LinqPad:
Here is a recursive ZipMany
implementation inspired from an answer of @Enigmativity to a different question.
public static IEnumerable<IEnumerable<T>> ZipMany<T>(params IEnumerable<T>[] sources)
{
return ZipRecursive(sources);
IEnumerable<IEnumerable<T>> ZipRecursive(IEnumerable<IEnumerable<T>> ss)
{
if (!ss.Skip(1).Any())
{
return ss.First().Select(i => Enumerable.Repeat(i, 1));
}
else
{
return ZipRecursive(ss.Skip(1).ToArray()).Zip(ss.First(), (x, y) => x.Prepend(y));
}
}
}
Advantage: avoids problems related to disposition of enumerators.
Disadvantage: Scales badly. The recursion overhead becomes noticeable at around 3000 enumerables, and grows exponentially afterwards.
Take a look at the linqlib project on codeplex, it has a rotate function that does exactly what you need.
You can roll your own ZipMany instance which manually iterates each of the enumerations. This will likely perform better on larger sequences than those using GroupBy
after projecting each sequence:
public static IEnumerable<TResult> ZipMany<TSource, TResult>(
IEnumerable<IEnumerable<TSource>> source,
Func<IEnumerable<TSource>, TResult> selector)
{
// ToList is necessary to avoid deferred execution
var enumerators = source.Select(seq => seq.GetEnumerator()).ToList();
try
{
while (true)
{
foreach (var e in enumerators)
{
bool b = e.MoveNext();
if (!b) yield break;
}
// Again, ToList (or ToArray) is necessary to avoid deferred execution
yield return selector(enumerators.Select(e => e.Current).ToList());
}
}
finally
{
foreach (var e in enumerators)
e.Dispose();
}
}