web-dev-qa-db-fra.com

Fractionner la liste en plusieurs listes avec un nombre fixe d'éléments

Comment diviser une liste d'éléments en listes comportant au plus N éléments?

ex: Avec une liste de 7 éléments, créez des groupes de 4 en laissant éventuellement le dernier groupe avec moins d'éléments.

split(List(1,2,3,4,5,6,"seven"),4)

=> List(List(1,2,3,4), List(5,6,"seven"))
110
Johnny Everson

Je pense que vous cherchez grouped. Il retourne un itérateur, mais vous pouvez convertir le résultat en liste,

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))
188
Kipton Barros

Il existe un moyen beaucoup plus facile de faire la tâche en utilisant la méthode par glissement. Cela fonctionne de cette façon:

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

Disons que vous voulez diviser la liste en petites listes de taille 3.

numbers.sliding(3, 3).toList

te donnera

List(List(1, 2, 3), List(4, 5, 6), List(7))
8
Dorjee

Ou si vous voulez créer le vôtre:

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

Utilisation:

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: après avoir examiné cela 2 ans plus tard, je ne recommanderais pas cette implémentation car size est O (n), et par conséquent cette méthode est O (n ^ 2), ce qui expliquez pourquoi la méthode intégrée devient plus rapide pour les grandes listes, comme indiqué dans les commentaires ci-dessous. Vous pouvez implémenter efficacement comme suit:

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

ou même (légèrement) plus efficacement avec 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)
  }
8

J'ajoute une version récursive de la méthode split dans la mesure où il a été question de récursion de queue et de récursivité. J'ai utilisé l'annotation tailrec pour forcer le compilateur à se plaindre au cas où l'implémentation ne serait pas réellement récusative. Je crois que la récursion de la queue se transforme en une boucle sous le capot et ne causera donc pas de problèmes, même pour une longue liste, car la pile ne grandira pas indéfiniment.

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

Je pense que ceci est l'implémentation utilisant splitAt au lieu de prendre/déposer

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
Hydrosan