LINQ merge List <IEnumerable <T>> into one IEnumerable <T> by some rule
Suppose I have a List<IEnumerable<double>> containing a variable number of infinite sources of double numbers. Suppose these are all the functions of a wave generator, and I need to overlay them in one wave generator, represented by IEnumerable<double> , simply by outputting the next number from each and summing them.
I know that I can do this using iterator methods, something like this:
public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs) { var funcs = from wfunc in wfuncs select wfunc.GetEnumerator(); while(true) { yield return funcs.Sum(s => s.Current); foreach (var i in funcs) i.MoveNext(); } } however, it seems rather "pedestrian." Is there a LINQ-ish way to achieve this?
I think there is no way around this without the LINQ extension. So, here is what I wrote at the end. I will try to contact the authors of MoreLinq to include them in some way, this may be useful in some scenarios:
public static class EvenMoreLinq { /// <summary> /// Combines mulitiple sequences of elements into a single sequence, /// by first pivoting all n-th elements across sequences /// into a new sequence then applying resultSelector to collapse it /// into a single value and then collecting all those /// results into a final sequence. /// NOTE: The length of the resulting sequence is the length of the /// shortest source sequence. /// Example (with sum result selector): /// S1 S2 S2 | ResultSeq /// 1 2 3 | 6 /// 5 6 7 | 18 /// 10 20 30 | 60 /// 6 - 7 | - /// - - | /// </summary> /// <typeparam name="TSource">Source type</typeparam> /// <typeparam name="TResult">Result type</typeparam> /// <param name="source">A sequence of sequences to be multi-ziped</param> /// <param name="resultSelector">function to compress a projected n-th column across sequences into a single result value</param> /// <returns>A sequence of results returned by resultSelector</returns> public static IEnumerable<TResult> MultiZip<TSource, TResult> this IEnumerable<IEnumerable<TSource>> source, Func<IEnumerable<TSource>, TResult> resultSelector) { if (source == null) throw new ArgumentNullException("source"); if (source.Any(s => s == null)) throw new ArgumentNullException("source", "One or more source elements are null"); if (resultSelector == null) throw new ArgumentNullException("resultSelector"); var iterators = source.Select(s => s.GetEnumerator()).ToArray(); try { while (iterators.All(e => e.MoveNext())) yield return resultSelector(iterators.Select(e => e.Current)); } finally { foreach (var i in iterators) i.Dispose(); } } } using this, I managed to compress my combined generator:
interface IWaveGenerator { IEnumerable<double> Generator(double timeSlice, double normalizationFactor = 1.0d); } [Export(typeof(IWaveGenerator))] class CombinedWaveGenerator : IWaveGenerator { private List<IWaveGenerator> constituentWaves; public IEnumerable<double> Generator(double timeSlice, double normalizationFactor = 1) { return constituentWaves.Select(wg => wg.Generator(timeSlice)) .MultiZip(t => t.Sum() * normalizationFactor); } // ... } You can combine the Zip method over IEnumerables.
public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs) { return wfuncs.Aggregate((func, next) => func.Zip(next, (d, dnext) => d + dnext)); } What this does is basically apply the same Zip method over and over again. With four IEnumerables, this will expand to:
wfuncs[0].Zip(wfuncs[1], (d, dnext) => d + dnext) .Zip(wfuncs[2], (d, dnext) => d + dnext) .Zip(wfuncs[3], (d, dnext) => d + dnext); Try it: fiddle
This is a situation where LINQ will probably be more difficult to understand than buy you anything. It is best to fix your sample. Something like this should work:
public IEnumerable<double> Generator(IReadOnlyCollection<IEnumerable<double>> wfuncs) { var enumerators = wfuncs.Select(wfunc => wfunc.GetEnumerator()) .ToList(); while(enumerators.All(e => e.MoveNext())) { yield return enumerators.Sum(s => s.Current); } }