C# 将列表拆分为列表列表,在元素上拆分

C# 将列表拆分为列表列表,在元素上拆分,c#,linq,aggregate,C#,Linq,Aggregate,能否重写以下内容,以便使用LINQ(而不是这些老式的foreach循环) IEnumerable拆分为多个部分(IEnumerable内容, 函数(分段分隔符) { var sections=新列表(); 添加(新列表()); foreach(内容中的var元素) { if(分隔器(元件)) { 添加(新列表()); } 其他的 { sections.Last().Add(元素); } } 回流段; } 当我意识到可以通过foreach循环来实现时,我认为我几乎有办法做到这一点(这涉及到FSh

能否重写以下内容,以便使用LINQ(而不是这些老式的
foreach
循环)

IEnumerable拆分为多个部分(IEnumerable内容,
函数(分段分隔符)
{
var sections=新列表();
添加(新列表());
foreach(内容中的var元素)
{
if(分隔器(元件))
{
添加(新列表());
}
其他的
{
sections.Last().Add(元素);
}
}
回流段;
}

当我意识到可以通过
foreach
循环来实现时,我认为我几乎有办法做到这一点(这涉及到FSharp collections)。

您不想在这里使用LINQ。如果不做一些不好的事情,你将无法以正确的方式进行排序和分组

最简单的方法是使用代码延迟执行。实现这一点的简单方法如下:

IEnumerable<IEnumerable<T>> SplitIntoSections<T>(this IEnumerable<T> source, 
    Func<T, bool> sectionDivider)
{
    // The items in the current group.
    IList<T> currentGroup = new List<T>();

    // Cycle through the items.
    foreach (T item in source)
    {
        // Check to see if it is a section divider, if
        // it is, then return the previous section.
        // Also, only return if there are items.
        if (sectionDivider(item) && currentGroup.Count > 0)
        {
            // Return the list.
            yield return currentGroup;

            // Reset the list.
            currentGroup = new List<T>();
        }

        // Add the item to the list.
        currentGroup.Add(item);
    }

    // If there are items in the list, yield it.
    if (currentGroup.Count > 0) yield return currentGroup;
}
IEnumerable拆分为部分(此IEnumerable源,
Func(分段分隔器)
{
//当前组中的项目。
IList currentGroup=新列表();
//循环浏览这些项目。
foreach(源中的T项)
{
//如果需要,检查是否为分段分隔器
//如果是,则返回上一节。
//此外,只有在有项目时才返回。
if(分段分隔器(项目)&¤tGroup.Count>0)
{
//返回列表。
收益率返回电流组;
//重置列表。
currentGroup=新列表();
}
//将项目添加到列表中。
当前组。添加(项);
}
//如果列表中有项目,请将其放弃。
如果(currentGroup.Count>0)产生返回currentGroup;
}

这里有个问题;对于非常大的组,将子组存储在列表中效率很低,它们也应该被流式输出。你的方法的问题是你有一个函数需要在每个项目上被调用;它会干扰流操作,因为一旦找到分组,就无法向后重置流(因为您实际上需要两种产生结果的方法)。

您可以使用仅在定义明确的区域内使用的副作用。。。很臭,但是:

int id = 0;
return content.Select(x => new { Id = isSectionDivider(x) ? id : ++id,
                                 Value = x })
              .GroupBy(pair => pair.Id, pair.Value)
              .ToList();
但肯定有更好的选择<代码>聚合将在必要时让您到达那里

return content.Aggregate(new List<List<T>>(), (lists, value) => {
                             if (lists.Count == 0 || isSectionDivider(value)) {
                                 lists.Add(new List<T>());
                             };
                             lists[lists.Count - 1].Add(value);
                             return lists;
                         });
返回content.Aggregate(新列表(),(列表,值)=>{
if(lists.Count==0 | | isSectionDivider(值)){
添加(新列表());
};
列表[lists.Count-1]。添加(值);
退货清单;
});

。。。但总的来说,我同意,这是最好在LINQ之外处理的情况。

这里有一个效率低下但纯粹的LINQ解决方案:

var dividerIndices = content.Select((item, index) => new { Item = item, Index = index })
                            .Where(tuple => isSectionDivider(tuple.Item))
                            .Select(tuple => tuple.Index);


return new[] { -1 }
        .Concat(dividerIndices)
        .Zip(dividerIndices.Concat(new[] { content.Count() }),
            (start, end) => content.Skip(start + 1).Take(end - start - 1));

好吧,我在这里使用一种LINQ方法,虽然它并不特别符合你问题的精神,但我认为:

static class Utility
{
    // Helper method since Add is void
    static List<T> Plus<T>(this List<T> list, T newElement)
    {
        list.Add(newElement);
        return list;
    }

    // Helper method since Add is void
    static List<List<T>> PlusToLast<T>(this List<List<T>> lists, T newElement)
    {
        lists.Last().Add(newElement);
        return lists;
    }

    static IEnumerable<IEnumerable<T>> SplitIntoSections<T>
         (IEnumerable<T> content, 
          Func<T, bool> isSectionDivider)
    {
        return content.Aggregate(                      // a LINQ method!
            new List<List<T>>(),                       // start with empty sections
            (sectionsSoFar, element) =>
            isSectionDivider(element)
                ? sectionsSoFar.Plus(new List<T>())
                  // create new section when divider encountered

                : sectionsSoFar.PlusToLast(element)
                  // add normal element to current section
            );
    }
}
静态类实用程序
{
//因为Add是空的,所以Helper方法
静态列表Plus(此列表,T新元素)
{
list.Add(新元素);
退货清单;
}
//因为Add是空的,所以Helper方法
静态列表PlusToLast(此列表列出T新元素)
{
lists.Last().Add(新元素);
退货清单;
}
静态IEnumerable拆分部分
(i)可数内容,
函数(分段分隔符)
{
返回content.Aggregate(//一个LINQ方法!
新建列表(),//以空节开始
(sectionsSoFar,元素)=>
ISSECTION分隔器(元件)
?部分sFar.Plus(新列表())
//遇到分隔符时创建新节
:sectionsFar.PlusToLast(元素)
//将法线元素添加到当前节
);
}
}

如果您决定使用此代码,我相信您会注意到完全没有错误检查…

我认为这将涉及
组x的语法,但我的LINQ不够好,无法给出正确的答案:(I cna evision 2解决方案,一个使用group by。(并且是>O(n)(尽管可能只是O(2n)),另一个使用聚合,因为聚合是Linq for F#Fold,它必须能够表达你对列表所能做的每一个操作更正我,如果是真的,除法器是添加到组中的吗?据我所见,除法器被忽略了。事实上,它们应该被忽略。有点像在String.SplitI中。当你发布你的nswer。完全同意。如果if语句分支中的赋值相等,则不会对LINQ造成太大的不利。+1Lol。当我看到这个答案时,它说“22小时前编辑的”是“casperOne”,答案中的最后一句话是“…但总的来说,我同意casperOne”。。。
static class Utility
{
    // Helper method since Add is void
    static List<T> Plus<T>(this List<T> list, T newElement)
    {
        list.Add(newElement);
        return list;
    }

    // Helper method since Add is void
    static List<List<T>> PlusToLast<T>(this List<List<T>> lists, T newElement)
    {
        lists.Last().Add(newElement);
        return lists;
    }

    static IEnumerable<IEnumerable<T>> SplitIntoSections<T>
         (IEnumerable<T> content, 
          Func<T, bool> isSectionDivider)
    {
        return content.Aggregate(                      // a LINQ method!
            new List<List<T>>(),                       // start with empty sections
            (sectionsSoFar, element) =>
            isSectionDivider(element)
                ? sectionsSoFar.Plus(new List<T>())
                  // create new section when divider encountered

                : sectionsSoFar.PlusToLast(element)
                  // add normal element to current section
            );
    }
}