Wie teilt man eine Sequenz nach Prädikat in zwei Teile?

120

Wie teile ich eine Sequenz durch ein Prädikat in zwei Listen auf?

Alternative: Ich kann meine eigene Methode verwenden filterund filterNot/ oder schreiben, aber gibt es keine allgemeinere (integrierte) Methode?

John Threepwood
quelle

Antworten:

193

Mit der partitionMethode:

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
quelle
1
val (even, odd) = List(1,2,3,4).partition(x => x % 2 == 0)ist eine Möglichkeit, das resultierende Tupel von partitionauf lesbare Weise zu zerstören .
k0pernikus
2
Man kann die Funktion innerhalb der Partition auf verkürzen _ % 2 == 0.
k0pernikus
138

Gut, das partitionwar das, was Sie wollten - es gibt eine andere Methode, die auch ein Prädikat verwendet, um eine Liste in zwei Teile zu teilen : span.

Die erste Partition fügt alle "wahren" Elemente in eine Liste und die anderen in die zweite Liste ein.

span fügt alle Elemente in eine Liste ein, bis ein Element "falsch" ist (in Bezug auf das Prädikat). Ab diesem Zeitpunkt werden die Elemente in die zweite Liste aufgenommen.

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
quelle
2
Genau das, wonach ich gesucht habe. Wenn die Liste nach einem verwandten Kriterium sortiert ist, ist dies viel sinnvoller.
erich2k8
16

Vielleicht möchten Sie einen Blick auf scalex.org werfen - hier können Sie die Scala-Standardbibliothek anhand ihrer Signatur nach Funktionen durchsuchen. Geben Sie beispielsweise Folgendes ein:

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

Sie würden Partition sehen .

oxbow_lakes
quelle
10
Die Domain scalex.org ist derzeit tot. Aber es gibt eine Alternative - scala-search.org ;-) .
Montag,
1
Lehren, wie man einen Fisch fängt!
DIESER BENUTZER BRAUCHT HILFE
1
@monnef Gibt es eine Alternative für Ihre Alternative für 2020? :)
Zivilist
14

Sie können auch foldLeft verwenden, wenn Sie etwas mehr benötigen. Ich habe gerade einen Code wie diesen geschrieben, als die Partition ihn nicht geschnitten hat:

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
quelle
1
Sehr schöne Art, ein Tupel und FoldLeft zu verwenden. Am Ende habe ich einen ListBuffer verwendet, um die beiden Listen effizient in derselben Reihenfolge zu halten, aber ansonsten war er genau das Richtige für das, was ich brauchte.
Matt Hagopian
1

Ich weiß, dass ich vielleicht zu spät zur Party komme und es gibt spezifischere Antworten, aber Sie könnten sie gut gebrauchen 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)

Dies macht Ihren Code ein bisschen zukunftssicherer, wenn Sie die Bedingung in etwas nicht Boolesches ändern müssen.

Gabber
quelle
0

Wenn Sie eine Liste in mehr als 2 Teile aufteilen und die Grenzen ignorieren möchten, können Sie Folgendes verwenden (ändern, wenn Sie nach Ints suchen müssen).

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"})
Matt
quelle