使用LINQ将嵌套循环展平到一个列表

时间:2014-04-02 19:43:52

标签: c# linq foreach

我现在用TPL类替换我的旧并行化辅助类。当动作代码中出现错误时,我的旧代码已被证明非常不可靠,而且它似乎不是为我现在正在做的事情而构建的。

第一份工作清单很容易翻译成Parallel.ForEach。但是这里有一个嵌套的索引循环,我无法轻易解决。

int streamIndex = 0;
foreach (var playlist in selectedPlaylists)
{
    var localPlaylist = playlist;
    foreach (var streamFile in playlist.StreamFiles)
    {
        var localStreamFile = streamFile;
        var localStreamIndex = streamIndex++;
        // Action that uses localPlaylist, localStreamFile and localStreamIndex
        ...
        // Save each job's result to its assigned place in the list
        lock (streamsList)
        {
            streamsList[localStreamIndex] = ...;
        }
    }
}

当共享foreach迭代变量时,局部变量用于正确的闭包支持。

我在考虑像

这样的东西
selectedPlaylists.SelectMany(p => p.StreamFiles)

然后我失去了每个streamFile来自哪里的关联,以及应该是确定性的索引,因为它用于在结果列表中对结果进行排序。有没有办法保持这些与Linq的关联,并在枚举列表时添加该计数器?也许像这样(伪造的伪代码):

selectedPlaylists
    .SelectMany(p => new
    {
        Playlist = p,
        StreamFile = ~~each one of p.StreamFiles~~,
        Index = ~~Counter()~~
    })

我可以保留那些旧的嵌套foreach循环并收集列表中的所有作业,然后使用Parallel.Invoke,但这似乎比它需要的更复杂。我想知道是否有一个我还不知道的简单的Linq功能。

2 个答案:

答案 0 :(得分:2)

你可以做这样的事......

//
Dictionary<int, object> streamsList = new Dictionary<int, object>();

// First create a composition that holds the playlist and the streamfile
selectedPlaylists.SelectMany(playList => playList.StreamFiles.Select(streamFile => new { PlayList = playList, StreamFile = streamFile }))
                 // thenfor all of theese add the respective index
                 .Select((composition, i) => new { StreamFile = composition.StreamFile, PlayList = composition.PlayList, LocalStreamIndex = i })
                 .AsParallel()

                 .WithCancellation(yourTokenGoesHere)
                 .WithDegreeOfParallelism(theDegreeGoesHere)

                 .ForAll(indexedComposition =>
                 {
                     object result =somefunc(indexedComposition.LocalStreamIndex, indexedComposition.PlayList, indexedComposition.StreamFile);;
                     lock(streamsList) // dont call the function insde the lock or the as parallel is useless.
                         streamsList[indexedComposition.LocalStreamIndex] = result;

                 });

答案 1 :(得分:1)

要展开StreamFiles并与PlayList保持关联并为其编制索引,您可以使用此查询:

int index = 0;
var query = selectedPlaylists
              .SelectMany(p => p.StreamFiles
                               .Select(s =>
                                          new {
                                                PlayList = p,
                                                Index = index++,
                                                StreamFile = s
                                              }));