Split list into multiple lists with fixed number of elements

前端 未结 5 446
独厮守ぢ
独厮守ぢ 2020-12-02 10:17

How to split a List of elements into lists with at most N items?

ex: Given a list with 7 elements, create groups of 4, leaving the last group possibly with less elem

相关标签:
5条回答
  • 2020-12-02 10:54

    I think this is the implementation using splitAt instead of take/drop

    def split [X] (n:Int, xs:List[X]) : List[List[X]] =
        if (xs.size <= n) xs :: Nil
        else   (xs.splitAt(n)._1) :: split(n,xs.splitAt(n)._2)
    
    0 讨论(0)
  • 2020-12-02 10:55

    I am adding a tail recursive version of the split method since there was some discussion of tail-recursion versus recursion. I have used the tailrec annotation to force the compiler to complain in case the implementation is not indeed tail-recusive. Tail-recursion I believe turns into a loop under the hood and thus will not cause problems even for a large list as the stack will not grow indefinitely.

    import scala.annotation.tailrec
    
    
    object ListSplitter {
    
      def split[A](xs: List[A], n: Int): List[List[A]] = {
        @tailrec
        def splitInner[A](res: List[List[A]], lst: List[A], n: Int) : List[List[A]] = {
          if(lst.isEmpty) res
          else {
            val headList: List[A] = lst.take(n)
            val tailList : List[A]= lst.drop(n)
            splitInner(headList :: res, tailList, n)
          }
        }
    
        splitInner(Nil, xs, n).reverse
      }
    
    }
    
    object ListSplitterTest extends App {
      val res = ListSplitter.split(List(1,2,3,4,5,6,7), 2)
      println(res)
    }
    
    0 讨论(0)
  • 2020-12-02 10:58

    Or if you want to make your own:

    def split[A](xs: List[A], n: Int): List[List[A]] = {
      if (xs.size <= n) xs :: Nil
      else (xs take n) :: split(xs drop n, n)
    }
    

    Use:

    scala> split(List(1,2,3,4,5,6,"seven"), 4)
    res15: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))
    

    edit: upon reviewing this 2 years later, I wouldn't recommend this implementation since size is O(n), and hence this method is O(n^2), which would explain why the built-in method becomes faster for large lists, as noted in comments below. You could implement efficiently as follows:

    def split[A](xs: List[A], n: Int): List[List[A]] =
      if (xs.isEmpty) Nil 
      else (xs take n) :: split(xs drop n, n)
    

    or even (slightly) more efficiently using splitAt:

    def split[A](xs: List[A], n: Int): List[List[A]] =
      if (xs.isEmpty) Nil 
      else {
        val (ys, zs) = xs.splitAt(n)   
        ys :: split(zs, n)
      }
    
    0 讨论(0)
  • 2020-12-02 11:01

    There is much easier way to do the task using sliding method. It works this way:

    val numbers = List(1, 2, 3, 4, 5, 6 ,7)
    

    Lets say you want to break the list into smaller lists of size 3.

    numbers.sliding(3, 3).toList
    

    will give you

    List(List(1, 2, 3), List(4, 5, 6), List(7))
    
    0 讨论(0)
  • 2020-12-02 11:06

    I think you're looking for grouped. It returns an iterator, but you can convert the result to a list,

    scala> List(1,2,3,4,5,6,"seven").grouped(4).toList
    res0: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))
    
    0 讨论(0)
提交回复
热议问题