Comment scinder une séquence en deux morceaux par prédicat?

120

Comment diviser une séquence en deux listes par un prédicat?

Alternative: je peux utiliser filteret filterNot, ou écrire ma propre méthode, mais n'y a-t-il pas une meilleure méthode plus générale (intégrée)?

John Threepwood
la source

Réponses:

194

En utilisant la partitionméthode:

scala> List(1,2,3,4).partition(x => x % 2 == 0)
res0: (List[Int], List[Int]) = (List(2, 4),List(1, 3))
Om Nom Nom
la source
1
val (even, odd) = List(1,2,3,4).partition(x => x % 2 == 0)est un moyen de détruire le tuple résultant de partitionde manière lisible.
k0pernikus
2
On peut raccourcir la fonction à l'intérieur de la partition à _ % 2 == 0.
k0pernikus
138

Bon qui partitionétait la chose que vous vouliez - il y a une autre méthode qui utilise également un prédicat de diviser une liste en deux: span.

Le premier, partition mettra tous les éléments "vrais" dans une liste, et les autres dans la deuxième liste.

span mettra tous les éléments dans une liste jusqu'à ce qu'un élément soit "faux" (en termes de prédicat). À partir de là, il mettra les éléments dans la deuxième liste.

scala> Seq(1,2,3,4).span(x => x % 2 == 0)
res0: (Seq[Int], Seq[Int]) = (List(),List(1, 2, 3, 4))
Daniel C. Sobral
la source
2
Exactement ce que je cherchais. Lorsque la liste est ordonnée par un critère connexe, cela a beaucoup plus de sens.
erich2k8
16

Vous voudrez peut-être jeter un œil à scalex.org - il vous permet de rechercher dans la bibliothèque standard scala des fonctions par leur signature. Par exemple, tapez ce qui suit:

List[A] => (A => Boolean) => (List[A], List[A])

Vous verriez une partition .

oxbow_lakes
la source
10
Le domaine scalex.org est actuellement mort. Mais il existe une alternative - scala-search.org ;-).
monnef
1
Apprendre à attraper un poisson!
CET UTILISATEUR A BESOIN D'AIDE
1
@monnef Une alternative à votre alternative pour 2020? :)
tehCivilian
14

Vous pouvez également utiliser foldLeft si vous avez besoin de quelque chose d'un peu plus. Je viens d'écrire un code comme celui-ci lorsque la partition ne l'a pas coupé:

val list:List[Person] = /* get your list */
val (students,teachers) = 
  list.foldLeft(List.empty[Student],List.empty[Teacher]) {
    case ((acc1, acc2), p) => p match {
      case s:Student => (s :: acc1, acc2)
      case t:Teacher  => (acc1, t :: acc2)
    }
  }
nairbv
la source
1
Très belle façon d'utiliser un tuple et foldLeft. J'ai fini par utiliser un ListBuffer pour garder efficacement les deux listes dans le même ordre, mais sinon, c'était parfait pour ce dont j'avais besoin.
Matt Hagopian
1

Je sais que je suis peut-être en retard pour la fête et il y a des réponses plus précises, mais vous pourriez faire bon usage de groupBy

val ret = List(1,2,3,4).groupBy(x => x % 2 == 0)

ret: scala.collection.immutable.Map[Boolean,List[Int]] = Map(false -> List(1, 3), true -> List(2, 4))

ret(true)
res3: List[Int] = List(2, 4)

ret(false)
res4: List[Int] = List(1, 3)

Cela rend votre code un peu plus évolutif si vous devez changer la condition en quelque chose de non booléen.

Gabber
la source
0

Si vous voulez diviser une liste en plus de 2 morceaux et ignorer les limites, vous pouvez utiliser quelque chose comme ça (modifiez si vous avez besoin de rechercher des entiers)

def split(list_in: List[String], search: String): List[List[String]] = {
  def split_helper(accum: List[List[String]], list_in2: List[String], search: String): List[List[String]] = {
    val (h1, h2) = list_in2.span({x: String => x!= search})
    val new_accum = accum :+ h1
    if (h2.contains(search)) {
      return split_helper(new_accum, h2.drop(1), search) 
    }
    else {
    return accum
    }
  }
  return split_helper(List(), list_in, search)
}

// TEST

// split(List("a", "b", "c", "d", "c", "a"), {x: String => x != "x"})
Mat
la source