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.
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.