7
\$\begingroup\$

I need to split a list into equal sublists, e. g [1..9] split into groups of 3 will be [[1, 2, 3], [4, 5, 6], [7, 8, 9]]. I have accomplished this task in the following way:

splitInGroupsOf n = takeWhile ((n ==) . length)
                  . map fst
                  . drop 1
                  . iterate (\(res, list) -> splitAt n list)
                  . (,) []

where iterate creates list of tuples with first n elements and rest of list. This way I had to use (,) [] on argument to ensure correct type, and unwrap result afterwards. My questions are

  1. is there a better/more elegant way of performing same task?
  2. is there some standard function I should make use of?

P.S.: I'm not sure where to ask simple Haskell-related questions and will appreciate if someone points me a better place for this than SE.

\$\endgroup\$
9
\$\begingroup\$

There are Data.List.Split.chunksOf and Data.List.Grouping.splitEvery implementations of this routine in specialized packages (and a number included in other application packages: search by Int -> [a] -> [[a]] signature on Hayoo).

I think splitEvery implementation is pretty elegant:

splitEvery :: Int -> [a] -> [[a]]
splitEvery _ [] = []
splitEvery n xs = as : splitEvery n bs 
  where (as,bs) = splitAt n xs
\$\endgroup\$
  • \$\begingroup\$ strangely enough, Hoogle is unable to find these functions. I've got a second question: when I search Hayoo for Int -> [a] -> [[a]] then chunksOf is not at top, but when I search Int -> [e] -> [[e]], then chunksOf is first. How am I supposed to guess what letter to use as a type variable? \$\endgroup\$ – sukhmel Apr 30 '14 at 17:13
0
\$\begingroup\$

The ready made function chunksOf works very well. When tasked to create 3 elements in sublists with 11 elements in the source list, two elements will be in the last sublist of the result. The following function also includes trailers.

mklsts n = takeWhile (not.null) . map (take n) . iterate (drop n)

I use this as pairs with a 2 for n and no n parameter. Pairs rock.

Edit/Add 4/12/2018

The match up of iterate and splitOn is one made in hell. In the questioner above, placing splitOn in a lambda may have compounded the problems. It is possible to make splitOn work with iterate but you have to ditch the fst of the tuple produced. That defeats the entire purpose. It is way cleaner and easier to use drop n with iterate. The results are the same. That's what the preceding function does. Otherwise, it's the same idea.

Here is a novel way of producing the identical results using tails imported from Data.List in a list comprehension. It picks up stragglers, too.

ts n ls = [take n l|l<-init$tails ls,odd (head l)]

The parameters are size-of-sublist and list

Edit 4/17/2018

Well, since I had some time at work a list comprehension version that does not use tails, a recursive version and a map version.

ttx s ls=[take s.drop x$ls|x<-[0,s..s*1000]]

Recursive

tkn n []=[];tkn n xs=[take n xs]++(tkn n $ drop n xs)

Map

tp n ls=takeWhile(not.null)$   map(take n.flip drop ls) [0,n..]

The list comprehension is virtually infinite. Change [0,s..s*200] to [0,s..] for true infinity. The recursive is, of course, inherently infinite and the map function uses a big takeWhile (not.null) to end itself.

\$\endgroup\$
  • \$\begingroup\$ "The ready made function chunksOf"? Which chunksOf do you speak of? Where do you address the code of the original poster? Did you intend to comment leventov's answer instead? \$\endgroup\$ – Zeta Apr 12 '18 at 7:03
  • \$\begingroup\$ The questions are two. 1 is there a better/more elegant way of performing same task? 2. Is there some standard function I should make use of? It is implied, the code of the original works fine. What I suggest as an alternative is shorter and clear. More elegant? It is relative I thinks so. Am I wrong? i tried so hard to use splitAt` but this resulted instead out of frustration. \$\endgroup\$ – fp_mora Apr 12 '18 at 7:59
  • \$\begingroup\$ Sure, but on Code Review, we review code. We do not only provide an alternative solution without an explanation why its better. I can apply the same critcism on leventov's answer, by the way. \$\endgroup\$ – Zeta Apr 12 '18 at 8:14
  • 1
    \$\begingroup\$ Thank you so much for the clarification. Criticisms leveled at me are usually the opposite. I can be tedious. My interest in this function is not incidental nor frivolous. The value is information contained in the sublists exceed that of the source list so they can streamline code and lessen logic. There are few things I value more than criticism. Thank you so very much. \$\endgroup\$ – fp_mora Apr 12 '18 at 16:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.