LINQ合并列表<ienumerable <t>&gt;通过一些规则进入一个IEnumerable <t>

时间:2015-11-09 12:13:01

标签: c# linq

假设我有List<IEnumerable<double>>包含可变数量的无限双数来源。让我们说它们都是波发生器功能,我需要将它们叠加到由IEnumerable<double>表示的单个波形发生器中,只需从每个波形中取出下一个数字并将它们相加。

我知道我可以通过迭代器方法做到这一点,如下所示:

    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();
        }
    } 
然而,它似乎是#34;行人&#34;。是否有LINQ-ish方法来实现这一目标?

4 个答案:

答案 0 :(得分:8)

您可以在IEnumerables上聚合Zip方法。

    public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs)
    {
        return wfuncs.Aggregate((func, next) => func.Zip(next, (d, dnext) => d + dnext));
    }

这样做基本上一遍又一遍地应用相同的Zip方法。有了四个IEnumebles,这将扩展为:

wfuncs[0].Zip(wfuncs[1], (d, dnext) => d + dnext)
         .Zip(wfuncs[2], (d, dnext) => d + dnext)
         .Zip(wfuncs[3], (d, dnext) => d + dnext);

尝试一下:fiddle

答案 1 :(得分:4)

我想如果没有扩展LINQ,就无法解决这个问题。所以这就是我最后写的。我将尝试联系MoreLinq作者以某种方式将其包括在内,它在某些透视场景中非常有用:

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();
        }
    }
}

使用这个我设法压缩我的组合生成器:

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);
    }
    // ...
}

答案 2 :(得分:3)

这种情况可能会让LINQ更难理解,而不会给你买任何东西。您最好的办法是修复您的样本方法。这样的事情应该有效:

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);
    }
} 

答案 3 :(得分:1)

有一个非常简单的方法来做到这一点。

public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs)
{
    return wfuncs.SelectMany(list => list);
}