Chunking a List - .NET vs Python

Posted by Abhijeet Patel on Geeks with Blogs See other posts from Geeks with Blogs or by Abhijeet Patel
Published on Sun, 07 Mar 2010 20:36:03 GMT Indexed on 2010/03/07 23:28 UTC
Read the original article Hit count: 574

Filed under:
Chunking a List

As I mentioned last time, I'm knee deep in python these days. I come from a statically typed background so it's definitely a mental adjustment. List comprehensions is BIG in Python and having worked with a few of them I can see why. Let's say we need to chunk a list into sublists of a specified size.
Here is how we'd do it in C#

  1.  static class Extensions  
  2.  {  
  3.      public static IEnumerable<List<T>> Chunk<T>(this List<T> l, int chunkSize)  
  4.      {  
  5.          if (chunkSize <0)  
  6.          {  
  7.              throw new ArgumentException("chunkSize cannot be negative""chunkSize");  
  8.          }  
  9.          for (int i = 0; i < l.Count; i += chunkSize)  
  10.          {  
  11.              yield return new List<T>(l.Skip(i).Take(chunkSize));  
  12.          }  
  13.      }   
  14.  }  
  15.   
  16. static void Main(string[] args)  
  17. {  
  18.          var l = new List<string> { "a""b""c""d""e""f","g" };  
  19.   
  20.          foreach (var list in l.Chunk(7))  
  21.          {  
  22.              string str = list.Aggregate((s1, s2) => s1 + "," + s2);  
  23.              Console.WriteLine(str);  
  24.          }  
  25.  }  

A little wordy but still pretty concise thanks to LINQ.We skip the iteration number plus chunkSize elements and yield out a new List of chunkSize elements on each iteration.

The python implementation is a bit more terse.

  1. def chunkIterable(iter, chunkSize):  
  2.     '''Chunks an iterable 
  3.         object into a list of the specified chunkSize 
  4.     '''    
  5.     assert hasattr(iter, "__iter__"), "iter is not an iterable"  
  6.     for i in xrange(0, len(iter), chunkSize):  
  7.         yield iter[i:i + chunkSize]  
  8.   
  9. if __name__ == '__main__':  
  10.     l = ['a', 'b', 'c', 'd', 'e', 'f']  
  11.     generator = chunkIterable(l,2)  
  12.     try:  
  13.         while(1):  
  14.             print generator.next()  
  15.     except StopIteration:  
  16.         pass  
xrange generates elements in the specified range taking in a seed and returning a generator. which can be used in a for loop(much like using a C# iterator in a foreach loop)
Since chunkIterable has a yield statement, it turns this method into a generator as well.
iter[i:i + chunkSize] essentially slices the list based on the current iteration index and chunksize and creates a new list that we yield out to the caller one at a time.
A generator much like an iterator is a state machine and each subsequent call to it remembers the state at which the last call left off and resumes execution from that point.

The caveat to keep in mind is that since variables are not explicitly typed we need to ensure that the object passed in is iterable using hasattr(iter, "__iter__").This way we can perform chunking on any object which is an "iterable", very similar to accepting an IEnumerable in the .NET land

© Geeks with Blogs or respective owner