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?

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

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

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 .


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

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.


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"})
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.