Come dividere una sequenza in due parti in base al predicato?


120

Come divido una sequenza in due elenchi in base a un predicato?

Alternativa: posso usare filtere filterNot, o scrivere il mio metodo, ma non esiste un metodo migliore più generale (integrato)?

Risposte:


194

Utilizzando il partitionmetodo:

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)è un modo per distruggere la tupla risultante di partitionin modo leggibile.
k0pernikus

2
Si può abbreviare la funzione all'interno della partizione a _ % 2 == 0.
k0pernikus

138

Bene che partitionera la cosa che volevi - c'è un altro metodo che utilizza anche un predicato per dividere una lista in due: span.

Il primo, la partizione , inserirà tutti gli elementi "veri" in un elenco e gli altri nel secondo elenco.

span metterà tutti gli elementi in una lista fino a quando un elemento non è "falso" (in termini di predicato). Da quel momento in poi, inserirà gli elementi nella seconda lista.

scala> Seq(1,2,3,4).span(x => x % 2 == 0)
res0: (Seq[Int], Seq[Int]) = (List(),List(1, 2, 3, 4))

2
Esattamente quello che stavo cercando. Quando l'elenco è ordinato in base a un criterio correlato, ciò ha molto più senso.
erich2k8

16

Potresti dare un'occhiata a scalex.org : ti permette di cercare funzioni nella libreria standard di scala in base alla loro firma. Ad esempio, digita quanto segue:

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

Vedresti la partizione .


10
Il dominio scalex.org è attualmente morto. Ma c'è un'alternativa - scala-search.org ;-).
monnef

1
Insegnare a prendere un pesce!
QUESTO UTENTE HA BISOGNO DI AIUTO

1
@monnef Qualche alternativa per la tua alternativa per il 2020? :)
tehCivilian

14

Puoi anche usare foldLeft se hai bisogno di qualcosa in più. Ho appena scritto un codice come questo quando la partizione non l'ha tagliato:

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
Un modo molto carino per usare una tupla e foldLeft. Ho finito per utilizzare un ListBuffer per mantenere in modo efficiente i due elenchi nello stesso ordine, ma per il resto era perfetto per quello di cui avevo bisogno.
Matt Hagopian

1

So che potrei essere in ritardo per la festa e ci sono risposte più specifiche, ma potresti farne buon uso 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)

Questo rende il tuo codice un po 'più a prova di futuro se devi cambiare la condizione in qualcosa di non booleano.


0

Se vuoi dividere un elenco in più di 2 parti e ignorare i limiti, puoi usare qualcosa di simile (modifica se devi cercare gli int)

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"})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.