String.split for collections

Published on den 27 March 2013

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

  1. 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

  2. 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.

Then feel free to it or if you have any comments or questions mention @MikaelEliasson on Twitter.

CTO and co-founder at Bokio with a background as an elite athlete. Still doing a lot of sports but more for fun.

#development, #web, #orienteering, #running, #cycling, #boardgames, #personaldevelopment