Here is an C# implementation of a split method that works like string.Split()
but for collections.
The problem
I had an Enumerable of objects that had one property called Code. If we only look at the key a string representing the collection would be 32,33,32,34,32
for a very simple instance of the problem.
I needed this to be split into:
[32,33,32][32,34,32]
Which is basically a string.Split but i keep the split element in both collection.
The solution
To keep the api consistent with how most my C# look this will be an extension method on IEnumerable so I can call it like this:
var sequences = posts.Split(p => p.Code == splitCode, EnumerableSplitOptions.AddToBoth);
The code to make this work is quite straightforward but I didn't find it instantly when I looked at the web so I decided to share it.
public static class EnumerableExtensions
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, Func<T, bool> shouldSplitAt, EnumerableSplitOptions splitOption = EnumerableSplitOptions.Remove)
{
return source.Split((item, index) => shouldSplitAt(item), splitOption);
}
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, Func<T, int, bool> shouldSplitAt, EnumerableSplitOptions splitOption = EnumerableSplitOptions.Remove)
{
var list = new List<IEnumerable<T>>();
var temp = new List<T>();
Action finishList = () =>
{
list.Add(temp);
temp = new List<T>();
};
int index = 0;
foreach (var item in source)
{
if (shouldSplitAt(item, index))
{
switch (splitOption)
{
case EnumerableSplitOptions.AddToEnd:
temp.Add(item);
finishList();
break;
case EnumerableSplitOptions.AddToBoth:
temp.Add(item);
finishList();
temp.Add(item);
break;
default:
finishList();
break;
}
}
else
{
temp.Add(item);
}
index++;
}
return list;
}
}
public enum EnumerableSplitOptions
{
Remove,
AddToEnd,
AddToBoth
}
Some notes
I haven't tested anything but the AddToBoth option. Feel free to notify me if the others doesn't work and I will fix the code
With this implementation and the sequence mentioned above the result will actually be:
"32"
"32,33,32"
"32,34,32"
That's ok I think, but a worse behaviour is that it would contain an empty enumarable i I had used the Remove option.