Grouping a list into lists of n elements in Haskell

Is there an operation on lists in library that makes groups of n elements? For example: n=3

groupInto 3 [1,2,3,4,5,6,7,8,9] = [[1,2,3],[4,5,6],[7,8,9]]

If not, how do I do it?


Solution 1:

A quick search on Hoogle showed that there is no such function. On the other hand, it was replied that there is one in the split package, called chunksOf.

However, you can do it on your own

group :: Int -> [a] -> [[a]]
group _ [] = []
group n l
  | n > 0 = (take n l) : (group n (drop n l))
  | otherwise = error "Negative or zero n"

Of course, some parentheses can be removed, I left there here for understanding what the code does:

The base case is simple: whenever the list is empty, simply return the empty list.

The recursive case tests first if n is positive. If n is 0 or lower we would enter an infinite loop and we don't want that. Then we split the list into two parts using take and drop: take gives back the first n elements while drop returns the other ones. Then, we add the first n elements to the list obtained by applying our function to the other elements in the original list.

enter image description here

Solution 2:

This function, among other similar ones, can be found in the popular split package.

> import Data.List.Split
> chunksOf 3 [1,2,3,4,5,6,7,8,9]
[[1,2,3],[4,5,6],[7,8,9]]

Solution 3:

You can write one yourself, as Mihai pointed out. But I would use the splitAt function since it doesn't require two passes on the input list like the take-drop combination does:

chunks :: Int -> [a] -> [[a]]
chunks _ [] = []
chunks n xs =
    let (ys, zs) = splitAt n xs
    in  ys : chunks n zs

This is a common pattern - generating a list from a seed value (which in this case is your input list) by repeated iteration. This pattern is captured in the unfoldr function. We can use it with a slightly modified version of splitAt (thanks Will Ness for the more concise version):

chunks n = takeWhile (not . null) . unfoldr (Just . splitAt n)

That is, using unfoldr we generate chunks of n elements while at the same time we shorten the input list by n elements, and we generate these chunks until we get the empty list -- at this point the initial input is completely consumed.

Of course, as the others have pointed out, you should use the already existing function from the split module. But it's always good to accustom yourself with the list processing functions in the standard Haskell libraries.

Solution 4:

This is ofte called "chunk" and is one of the most frequently mentioned list operations that is not in base. The package split provides such an operation though, copy and pasting the haddock documentation:

 > chunksOf 3 ['a'..'z']
 ["abc","def","ghi","jkl","mno","pqr","stu","vwx","yz"]

Additionally, against my wishes, hoogle only searches a small set of libraries (those provided with GHC or perhaps HP), but you can explicitly add packages to the search using +PKG_NAME - hoogle with Int -> [a] -> [[a]] +split gets what you want. Some people use Hayoo for this reason.