Paging with Linq to objects

Paging with Linq (to objects) seems quite simple:

                        
                                
      public static IEnumerable<TSource>GetPage<TSource>(this IEnumerable<TSourcev> source, int pageNumber, int pageSize)
      {
        return source.Skip((pageNumber - 1) * pageSize).Take(pageSize);
      }
      
                        
                    

This is handy when you need a page 'in the middle only' but if you need to enumerate all pages, you have to caculate the number of pages yourself. The next method will help you to get all pages:

                        
                                
      public static IEnumerable<IEnumerable<T>> PagesOf<T>(this IEnumerable<T>source, int pageSize)
      {
        var list = source.ToList();
        var pageCount = (list.Count + pageSize - 1) / pageSize;
        for (var pageNumber = 1; pageNumber <= pageCount; pageNumber++)
        {
          yield return GetPage(list, pageNumber, pageSize);
        }
      }
      
                        
                    

It works, but is is very ineffcient. It iterates the collection from the start, each time you get a new page. A better option would be:

                        
                                
      public static IEnumerable<IEnumerable<T>> PagesOf<T>(this IEnumerable<T> source, int pageSize)
      {
        var list = source.ToList();
        var pageCount = (list.Count + pageSize - 1) / pageSize;

        var pageRange = Enumerable.Range(1, pageCount);
        return pageRange.Select((page, index) => list.Skip(index * pageCount).Take(pageCount));
      }
      
                        
                    

This option only iterates all items only once, but if you need just a few pages, it iterates the items of all pages. The last option does not have that disadvantage:

                        
                                
      public static IEnumerable<IEnumerable<T>> PagesOf<T>(this IEnumerable<T>source, int pageSize)
      {
        var pageItems = new List<T>(pageSize);

        foreach (T item in source)
        {
          pageItems.Add(item);

          if (pageItems.Count >= pageSize)
          {
            yield return pageItems;
            pageItems = new List<T>(pageSize);
          }
        }
        if (pageItems.Count > 0)
        {
          yield return pageItems;
        }
      }
      
                        
                    

Leave a Comment

Comment

Comments