views:

331

answers:

2

I'd like to be able to write the following code:

// contains 500 entries
IList<string> longListOfStrings = ...

// shorterListsOfStrings is now an array of 5 IList<string>,
// with each element containing 100 strings
IList<string>[] shorterListsOfStrings = longListOfStrings.Split(5);

To do this I have to create a generic extension method called Split that looks something like the following:

public static TList[] Split<TList>(this TList source, int elementCount)
  where TList : IList<>, ICollection<>, IEnumerable<>, IList, ICollection, IEnumerable
{
  return null;
}

But when I try to compile that, the compiler tells me that IList<>, ICollection<> and IEnumerable<> require a type argument. So, I changed the definition to the following:

public static TList<TType>[] Split<TList<TType>>(this TList<TType> source, int elementCount)
  where TList : IList<TType>, ICollection<TType>, IEnumerable<TType>, IList, ICollection, IEnumerable
{
  return null;
}

but then the compiler complains that it can't find type TList. I have an idea that I'm overcomplicating things but I can't see how... any help is appreciated!

+5  A: 

Yes, I think you're overcomplicating things. Try this:

public static IList<T>[] Split<T>(this IList<T> source, int elementCount)
{
    // What the heck, it's easy to implement...
    IList<T>[] ret = new IList<T>[(source.Count + elementCount - 1) 
                                  / elementCount];
    for (int i = 0; i < ret.Length; i++)
    {
        int start = i * elementCount;
        int size = Math.Min(elementCount, source.Count - i * start);
        T[] tmp = new T[size];
        // Would like CopyTo with a count, but never mind
        for (int j = 0; i < size; j++)
        {
            tmp[j] = source[j + start];
        }
        ret[i] = tmp;
    }
    return ret;
}

After all, you're not going to change which kind of list you create within the method based on the source, are you? You'll presumably create a List<T> (or maybe a T[]) even if I pass in some other implementation.

You might want to look at the Batch method in MoreLINQ for an IEnumerable<T>-based implementation.

Jon Skeet
Thanks Jon, took too long to type it up! Ha.
Tim
+3  A: 

How about this:

public static IList<TResult>[] Split<TSource, TResult>(
    this IList<TSource> source,      // input IList to split
    Func<TSource, TResult> selector, // projection to apply to each item
    int elementCount                 // number of items per IList
) {
    // do something        
}

And if you don't need a version to project each item:

 public static IList<T>[] Split<T>(
    this IList<T> source, // input IList to split
    int elementCount      // number of items per IList
) {
      return Split<T, T>(source, x => x, elementCount);      
}
Jason