¿Cómo dividir una secuencia en dos partes por predicado?

120

¿Cómo divido una secuencia en dos listas por un predicado?

Alternativa: puedo usar filtery filterNot, o escribir mi propio método, pero ¿no hay un método mejor más general (integrado)?

John Threepwood
fuente

Respuestas:

194

Usando el partitionmétodo:

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
fuente
1
val (even, odd) = List(1,2,3,4).partition(x => x % 2 == 0)es una forma de destruir la tupla resultante de partitionde forma legible.
k0pernikus
2
Se puede acortar la función dentro de la partición a _ % 2 == 0.
k0pernikus
138

Bien que partitionera lo que quería - no hay otro método que también usa un predicado para dividir una lista en dos: span.

La primera, la partición pondrá todos los elementos "verdaderos" en una lista y los demás en la segunda lista.

span pondrá todos los elementos en una lista hasta que un elemento sea "falso" (en términos del predicado). A partir de ese momento, colocará los elementos en la segunda lista.

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
fuente
2
Exactamente lo que estaba buscando. Cuando la lista está ordenada por un criterio relacionado, esto tiene mucho más sentido.
erich2k8
16

Es posible que desee echar un vistazo a scalex.org : le permite buscar funciones en la biblioteca estándar de scala por su firma. Por ejemplo, escriba lo siguiente:

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

Vería la partición .

oxbow_lakes
fuente
10
El dominio scalex.org está actualmente muerto. Pero hay una alternativa: scala-search.org ;-).
lunes
1
¡Enseñando a pescar un pez!
ESTE USUARIO NECESITA AYUDA
1
@monnef ¿Alguna alternativa para tu alternativa para 2020? :)
tehCivilian
14

También puede usar foldLeft si necesita algo un poco más. Acabo de escribir un código como este cuando la partición no lo cortó:

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
fuente
1
Muy buena forma de usar una tupla y foldLeft. Terminé usando un ListBuffer para mantener de manera eficiente las dos listas en el mismo orden, pero por lo demás fue perfecto para lo que necesitaba.
Matt Hagopian
1

Sé que podría llegar tarde a la fiesta y hay respuestas más específicas, pero podrías hacer un buen uso 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)

Esto hace que su código esté un poco más preparado para el futuro si necesita cambiar la condición a algo que no sea booleano.

Gabber
fuente
0

Si desea dividir una lista en más de 2 partes e ignorar los límites, puede usar algo como esto (modifíquelo si necesita buscar entradas)

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"})
Mate
fuente