Surya
Surya

Reputation: 1053

Dividing a list in specific number of sublists

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

Upvotes: 4

Views: 4123

Answers (5)

missingfaktor
missingfaktor

Reputation: 92026

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)

Upvotes: 4

user unknown
user unknown

Reputation: 36229

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.

Upvotes: 0

Patrick
Patrick

Reputation: 15717

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)
  }
}

Upvotes: 0

Rex Kerr
Rex Kerr

Reputation: 167891

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.

Upvotes: 2

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297165

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.

Upvotes: 2

Related Questions