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

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

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

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 .


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

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.


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"})
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.