views:

140

answers:

5

I want to divide a list in "a specific number of" sublists.

That is, for example if I have a list List(34, 11, 23, 1, 9, 83, 5) and the number of sublists expected is 3 then I want List(List(34, 11), List(23, 1), List(9, 83, 5)).

How do I go about doing this? I tried grouped but it doesn't seem to be doing what I want.

PS: This is not a homework question. Kindly give a direct solution instead of some vague suggestions.

EDIT:

A little change in the requirements...

Given a list List(34, 11, 23, 1, 9, 83, 5) and number of sublists = 3, I want the output to be List(List(34), List(11), List(23, 1, 9, 83, 5)). (i.e. 1 element per list except for the last list which holds all the remaining elements.)

A: 

That's a tough call, because you don't know how many elements to put into each list before you know the size of the list. If you do know the size of the list, you can use grouped: list.grouped((list.size + 2) / 3).toList. It will not divide the elements like you did, though.

Question: does the order matter? If the order of the elements need not be preserved, then there are better ways of accomplishing this.

Daniel
A: 

If you can tolerate a call to get the length of the list, then

l.grouped( (l.length+2)/3 ).toList

will produce something akin to what you want (if val l = List(34, 11, 23, 1, 9, 83, 5) then you'll get List(List(34, 11, 23), List(1, 9, 83), List(5)) back. But if you want approximately equal distribution across your lists, then you'll have to create your own method to do it--there isn't a library function that partitions a list equally into n pieces.

Something like this would work, if you want to keep the pieces in order:

def chopList[T](
  l: List[T], pieces: Int,
  len: Int = -1, done: Int = 0, waiting: List[List[T]]=Nil
): List[List[T]] = {
  if (l isEmpty) waiting.reverse
  else {
    val n = (if (len<0) l.length else len)
    val ls = l.splitAt( (n.toLong*(done+1)/pieces - n.toLong*done/pieces).toInt )
    chopList(ls._2,pieces,n,done+1,ls._1 :: waiting)
  }
}

and this happens to return exactly what you want: List(List(34, 11), List(23, 1), List(9, 83, 5)).

If you don't want to ask for the length of the list, then you can write a method that creates a bunch of buckets and drops a new item in each in turn.

Rex Kerr
A: 

Don't know it it answer to your problem, but here a try (what do you expect when count is outside of range) ?

def group[T](list:List[T], count:Int):List[List[T]] = {
  if (count <= 0 || count >= list.length)
    List(list)
  else {
    val numElm = list.length / count
    def loop(list:List[T], i:Int):List[List[T]] = {
      i match {
        case 0 => List(list) 
        case 1 => List(list)
        case _ =>  {
          val l = list.splitAt(numElm)
          l._1 :: loop(l._2, i-1)
        }
      }
    }
    loop(list, count)
  }
}
Patrick
A: 

Is grouped new in 2.8? A selfmade solution for 2.7.7 could look like this:

def splitTo (li: List [Int], count: Int) : List [List [Int]] = {
    val size = li.length / count

    if (count > 1) li.take (size) :: splitTo (li.drop (size), count-1) else 
        li :: Nil
} 

Parametrizing to List [T] is left as an excecise to the reader.

user unknown
Yes, `grouped` is new in the Scala 2.8 collections library.
Randall Schulz
+2  A: 

In response to your changed requirements,

def splitN[A](list: List[A], n: Int): List[List[A]] =
  if(n == 1) List(list) else List(list.head) :: splitN(list.tail, n - 1)
missingfaktor