I wrote a LINQ extension method SplitBetween
analogous to String.Split
.
> new List(){3,4,2,21,3,2,17,16,1}
> .SplitBet
Here is a solution that I suppose does what you ask for.
The problem was that you only had one method with yield and you were manually creating the internal collection while the outer IEnumerable
was enumerated. The second problem was that your way of "testing" fails even on mine code below. However, as David B pointed out in his comment, you must go through all the elements to define number of elements of outer IEnumerable
. But you can defer creation and population of inner IEnumerable
s.
public static IEnumerable> SplitBetween(this IEnumerable source,Func separatorSelector, bool includeSeparators=false)
{
IList sourceList = source.ToList();
var indexStart = 0;
var indexOfLastElement = sourceList.Count - 1;
for(int i = 0; i <= indexOfLastElement; i++)
if (separatorSelector(sourceList[i]))
{
if(includeSeparators)
yield return SplitBetweenInner(sourceList, indexStart, i);
else
yield return SplitBetweenInner(sourceList, indexStart, i - 1);
indexStart = i + 1;
}
else if(i == indexOfLastElement)
yield return SplitBetweenInner(sourceList, indexStart, i);
}
private static IEnumerable SplitBetweenInner(IList source, int startIndex, int endIndex)
{
//throw new Exception("BOOM");
for(int i = startIndex; i <= endIndex; i++)
yield return source[i];
}
Note that it behaves slightly different as your code (it doesn't create another empty List when the last element satisfies the separator condition - it's up to definition what is correct here, but I find that better as the behavior is the same as if the element appears at the beginning of source list)
If you test the code, you will see that the inner IEnumerable
execution is deferred.
If the throw exception line is uncommented:
(new List(){3,4,2,21,3,2,17,16,1}).SplitBetween(x=>x>=10, true).Count();
returns 4
(new List(){3,4,2,21,3,2,17,16,1}).SplitBetween(x=>x>=10, true).First().Count();
throws BOOM