# Better way to write this function: list to matrix (= list of lists)

Is there a better way to write this function (i.e. in one line via folds)?

``````--              list -> rowWidth -> list of lists
listToMatrix :: [a] -> Int -> [[a]]
listToMatrix [] _ = []
listToMatrix xs b = [(take b xs)] ++ (listToMatrix (drop b xs) b)
``````

## 4 answers

• answered 2017-06-17 18:09

No, although I wish `chunksOf` was in the standard library. You can get that from here if you want: https://hackage.haskell.org/package/split-0.2.3.2/docs/Data-List-Split.html

Note a better definition would be:

``````listToMatrix xs b = (take b xs) : (listToMatrix (drop b xs) b)
``````

although this might compile to the same core. You could also use `splitAt`, though again this is likely to perform the same.

``````listToMatrix xs b = let (xs,xss) = splitAt b xs in xs : listToMatrix xss
``````

• answered 2017-06-17 18:09

Yes, there is a better way to write this function. But I don't think that making it one line is going to improve anything.

### Use the prepending operator `(:)` instead of list concatenation `(++)` for single-element prepending

The expression `[(take b xs)] ++ (listToMatrix (drop b xs) b)` is inefficient. I don't mean inefficient in terms of performances, because the compiler probably optimizes that, but, here, you are constructing a list, and then calling a function on it (`(++)`) which is going to deconstruct it by pattern matching. You could instead build your list directly using the `(:)` data constructor, which allows you to prepend a single element to your list. The expression becomes `take b xs : listToMatrix (drop b xs) b`

### Use `splitAt` to avoid running through the list twice

``````import Data.List (splitAt)

listToMatrix :: [a] -> Int -> [[a]]
listToMatrix xs b = row : listToMatrix remaining b
where (row, remaining) = splitAt b xs
``````

### Ensure the correctness of your data by using `Maybe`

``````listToMatrix :: [a] -> Int -> Maybe [[a]]
listToMatrix xs b
| length xs `mod` b /= 0 = Nothing
| null xs   = Just []
| otherwise = Just \$ row : listToMatrix remaining b
where (row, remaining) = splitAt b xs
``````

You can even avoid checking every time if you have the right number of elements by defining a helper function:

``````listToMatrix :: [a] -> Int -> Maybe [[a]]
listToMatrix xs b
| length xs `mod` b /= 0 = Nothing
| otherwise = Just (go xs)
where go [] = []
go xs = row : go remaining
where (row, remaining) = splitAt b xs
``````

### Ensure the correctness of your data by using safe types

A matrix has te same number of elements in each row, whereas nested lists don't allow to ensure that kind of conditions. To be certain that every row has the same number of elements, you can either use a library such as `matrix` or `hmatrix`

• answered 2017-06-17 18:09

You can use this, the idea is to take all the chunks of the main list, it is a different approach but basicly do the same:

``````Prelude> let list2Matrix n xs = map (\(x ,y)-> (take n) \$ (drop (n*x)) y) \$ zip [0..] \$ replicate (div (length xs)  n) xs
Prelude> list2Matrix 10 [1..100]
[[1,2,3,4,5,6,7,8,9,10],[11,12,13,14,15,16,17,18,19,20],[21,22,23,24,25,26,27,28,29,30],[31,32,33,34,35,36,37,38,39,40],[41,42,43,44,45,46,47,48,49,50],[51,52,53,54,55,56,57,58,59,60],[61,62,63,64,65,66,67,68,69,70],[71,72,73,74,75,76,77,78,79,80],[81,82,83,84,85,86,87,88,89,90],[91,92,93,94,95,96,97,98,99,100]]
``````

• answered 2017-06-17 18:09

Actually this is a nice case for unfolding. `Data.List` method `unfoldr`, unlike folding a list, creates a list to a from a seed value by applying a function to it up until this function returns `Nothing`. Until we reach the terminating condition that will return `Nothing` the function returns `Just (a,b)` where `a` is the current generated item of the list and `b` is the next value of the seed. In this particular case our seed value is the given list.

``````import Data.List
chunk :: Int -> [a] -> [[a]]
chunk n = unfoldr (\xs -> if null xs then Nothing else Just (splitAt n xs))

*Main> chunk 3 [1,2,3,4,5,6,7,8,9]
[[1,2,3],[4,5,6],[7,8,9]]
*Main> chunk 3 [1,2,3,4,5,6,7,8,9,10]
[[1,2,3],[4,5,6],[7,8,9],[10]]
``````