1

I've just written a couple of pagination extension methods and I was curious to know if there was any improvements I could make.

I'm pretty happy with the base pagination method, where you supply both the page size and page number (as seen below)

    public static IEnumerable<T> Paginate<T>(this IEnumerable<T> source, int pageSize, int pageNumber)
    {
        if (pageSize == 0) throw new ArgumentOutOfRangeException("pageSize");
        if (pageNumber == 0) throw new ArgumentOutOfRangeException("pageNumber");

        return source.Skip(pageSize * (pageNumber - 1)).Take(pageSize);
    }

but I was wondering if there was a better way to do the "auto" pagination, where it returns a IEnumerable<IEnumerable<T>>

    public static IEnumerable<IEnumerable<T>> Paginate<T>(this IEnumerable<T> source, int pageSize)
    {
        source.ThrowIfNull("source");
        if (pageSize == 0) throw new ArgumentOutOfRangeException("pageSize");

        var pageCount = (int)Math.Ceiling(source.Count() / (double)pageSize);

        if (pageSize == 1)
            pageCount = source.Count();

        for (int i = 1; i <= pageCount; i++)
        {
            yield return source.Paginate(pageSize, i);
        }
    }

It seems a bit suspect to have to iterate twice (once for the count and once for the yield return.

Is there any obvious way I could improve these methods?

Alastair Pitts
  • 19,423
  • 9
  • 68
  • 97
  • For the yield return you have to iterate through pages only. There is a foreach workaround, but I doubt it will be faster, since foreach itself is slower. I would rather improve the Paginate method, if you are hungry for performance. IEnumerable extension methods like Skip() and Take() can be written by hand, and they will work faster in this particular case. (you have to loop from X to Y index, instead of looping from X to end and then from X to Y, as skip and take do) – Alex Oct 27 '10 at 05:37

1 Answers1

3

Take a look at MoreLinq Batch :- http://code.google.com/p/morelinq/source/browse/trunk/MoreLinq/Batch.cs?r=84

Which is implemented as:

public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
              this IEnumerable<TSource> source, int size)
{
  TSource[] bucket = null;
  var count = 0;

  foreach (var item in source)
  {
      if (bucket == null)
          bucket = new TSource[size];

      bucket[count++] = item;
      if (count != size)
          continue;

      yield return bucket;

      bucket = null;
      count = 0;
  }

  if (bucket != null && count > 0)
      yield return bucket.Take(count);
}
Ian Mercer
  • 38,490
  • 8
  • 97
  • 133