RAGHHURAAMM
RAGHHURAAMM

Reputation: 1099

Splitting a Scala List into parts using a given list of part sizes.[Partitioning]

I got two lists:

val list1:List[Int]   =  List(5, 2, 6)

val list2:List[Any]  =  List("a", "b", "c", "d", "e", "f", "g", "h", "i", "j","k")

such that list1.sum >= list2.size

I want a list of lists formed with elements in list2 consecutively with the sizes mentioned in list1.

For example:

if list1 is List(5,2,4) the result I want is:

List(List("a", "b", "c", "d", "e"),List("f", "g"),List("h", "i", "j","k"))

if list1 is List(5,4,6) the result I want is:

List(List("a", "b", "c", "d", "e"),List("f", "g","h", "i"),List("j","k"))

How can I do that with concise code.

Upvotes: 2

Views: 261

Answers (4)

Manjitha Teshara
Manjitha Teshara

Reputation: 592

i like show it using ex list1=List("one","two","three") List[String] = List(one, two, three)

list2=List("red","green","blue") List[String] = List(red, green, blue)

list1::list2 List[java.io.Serializable] = List(List(one, two, three), red, green, blue)

var listSum=list1::list2 List[java.io.Serializable] = List(List(one, two, three), red, green, blue)

listSum List[java.io.Serializable] = List(List(one, two, three), red, green, blue)

we can "::" for insert one list to another list in Scala

Upvotes: 1

jwvh
jwvh

Reputation: 51271

Turn list2 into an Iterator then map over list1.

val itr = list2.iterator
list1.map(itr.take(_).toList)
//res0: List[List[Any]] = List(List(a, b, c, d, e), List(f, g), List(h, i, j, k))

update: While this appears to give the desired results, it has been pointed out elsewhere that reusing the iterator is actually unsafe and its behavior is not guaranteed.

With some modifications a safer version can be achieved.

val itr = list2.iterator
list1.map(List.fill(_)(if (itr.hasNext) Some(itr.next) else None).flatten)

-- or --

import util.Try
val itr = list2.iterator
list1.map(List.fill(_)(Try(itr.next).toOption).flatten)

Upvotes: 4

RAGHHURAAMM
RAGHHURAAMM

Reputation: 1099

Just found by me, even the following code worked, but the code posted by jwvh appears more concise than this, and I understand making list1 a Vector makes more sense regarding performance of element access in the following code:

list1.scan(0)(_+_).sliding(2).toList.map(x=>list2.drop(x(0)).take(x(1)-x(0)))

or

list1.scan(0)(_+_).zip(list1).map(x=>list2.drop(x._1).take(x._2))

list1.scan(0)(_+_).sliding(2).map(x=>list2.slice(x(0),x(1))).toList

Upvotes: 0

prayagupadhyay
prayagupadhyay

Reputation: 31252

you can slice on list2 based on the size you get from list1,

  def main(args: Array[String]): Unit = {

    val groupingList: List[Int] = List(5, 2, 4)
    val data = List("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k")

    //0, 5
    //5, (5 + 2)
    //5 + 2, (5 + 2 + 4)

    val grouped = groupingList.zipWithIndex.map { case (_, index) =>
      val start = groupingList.slice(0, index).sum
      val end = groupingList.slice(0, index + 1).sum

      data.slice(start, end)
    }

    println(grouped)
  }

result: List(List(a, b, c, d, e), List(f, g), List(h, i, j, k))

Also read: How slice works

Upvotes: 2

Related Questions