可以重写以下内容以便使用LINQ(而不是这些老式的foreach
循环)
IEnumerable<IEnumerable<T>> SplitIntoSections<T>(IEnumerable<T> content,
Func<T, bool> isSectionDivider)
{
var sections = new List<List<T>>();
sections.Add(new List<T>());
foreach (var element in content)
{
if (isSectionDivider(element))
{
sections.Add(new List<T>());
}
else
{
sections.Last().Add(element);
}
}
return sections;
}
当我意识到可以通过foreach
循环完成时,我认为我几乎有办法做到这一点(它涉及FSharp的选择)。
答案 0 :(得分:2)
您不想在这里使用LINQ。如果不做一些粗糙的事情,你将无法以正确的方式订购和分组。
最简单的方法是使用yield
statement来获取代码并使其延迟执行。一个简单的方法如下:
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;
}
这里有一个问题;对于非常大的组,将子组存储在列表中是低效的,它们也应该流式传输。你的方法的问题是你有一个需要在每个项目上调用的函数;它会干扰流操作,因为一旦找到分组,就无法向后重置流(因为你实际上需要两个方法产生结果)。
答案 1 :(得分:0)
您可以使用仅在明确定义的区域内使用的副作用...它非常臭,但是:
int id = 0;
return content.Select(x => new { Id = isSectionDivider(x) ? id : ++id,
Value = x })
.GroupBy(pair => pair.Id, pair.Value)
.ToList();
必须有一个更好的选择...... Aggregate
会在必要时帮助你...
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;
});
...但总的来说我同意casperOne,这是在LINQ之外最好处理的情况。
答案 2 :(得分:0)
这是一个低效但纯粹的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));
答案 3 :(得分:0)
嗯,我在这里使用一种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
);
}
}
如果您决定使用此代码,我相信您会注意到完全没有错误检查...