I wrote a LINQ extension method SplitBetween
analogous to String.Split
.
> new List(){3,4,2,21,3,2,17,16,1}
> .SplitBet
public static IEnumerable> SplitBetween(this IEnumerable source, Func separatorSelector, bool includeSeparators = false)
{
var state = new SharedState(source, separatorSelector, includeSeparators);
state.LastList = state.NewList = new InnerList(state, 0);
for (; ; )
{
if (state.NewList != null)
{
var newList = state.NewList;
state.NewList = null;
yield return newList.Items();
}
else if (state.IsEnd)
break;
else
state.CheckNext();
}
}
class SharedState
{
public SharedState(IEnumerable source, Func separatorSelector, bool includeSeparators)
{
this.source = source;
this.separatorSelector = separatorSelector;
this.includeSeparators = includeSeparators;
this.iterator = source.GetEnumerator();
this.data = source as IList;
if (data == null)
{
cache = new List();
data = cache;
}
}
public readonly IEnumerable source;
readonly IEnumerator iterator;
public readonly IList data;
readonly List cache;
public readonly Func separatorSelector;
public readonly bool includeSeparators;
public int WaveIndex = -1;
public bool IsEnd = false;
public InnerList NewList;
public InnerList LastList;
public void CheckNext()
{
WaveIndex++;
if (!iterator.MoveNext())
{
if (LastList.LastIndex == null)
LastList.LastIndex = WaveIndex;
IsEnd = true;
}
else
{
var item = iterator.Current;
if (cache != null)
cache.Add(item);
if (separatorSelector(item))
{
LastList.LastIndex = includeSeparators ? WaveIndex + 1 : WaveIndex;
LastList = NewList = new InnerList(this, WaveIndex + 1);
}
}
}
}
class InnerList
{
public InnerList(SharedState state, int startIndex)
{
this.state = state;
this.StartIndex = startIndex;
}
readonly SharedState state;
public readonly int StartIndex;
public int? LastIndex;
public IEnumerable Items()
{
for (var i = StartIndex; ; ++i)
{
if (LastIndex != null && i >= LastIndex)
break;
if (i >= state.WaveIndex)
state.CheckNext();
if (LastIndex == null || i < LastIndex)
yield return state.data[i];
}
}
}