Bagaimana cara membagi urutan menjadi dua bagian berdasarkan predikat?

120

Bagaimana cara membagi urutan menjadi dua daftar dengan predikat?

Alternatif: Saya dapat menggunakan filterdan filterNot, atau menulis metode saya sendiri, tetapi bukankah ada metode yang lebih umum (bawaan) yang lebih baik?

John Threepwood
sumber

Jawaban:

194

Dengan menggunakan partitionmetode:

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
sumber
1
val (even, odd) = List(1,2,3,4).partition(x => x % 2 == 0)adalah cara untuk menghancurkan tupel yang dihasilkan partitiondengan cara yang dapat dibaca.
k0pernikus
2
Seseorang dapat mempersingkat fungsi di dalam partisi menjadi _ % 2 == 0.
k0pernikus
138

Baik yang partitionadalah hal yang Anda inginkan - ada metode lain yang juga menggunakan predikat untuk membagi daftar di dua: span.

Yang pertama, partisi akan meletakkan semua elemen "benar" dalam satu daftar, dan yang lainnya di daftar kedua.

span akan menempatkan semua elemen dalam satu daftar hingga elemen bernilai "false" (dalam istilah predikat). Sejak saat itu, ia akan menempatkan elemen-elemen tersebut dalam daftar kedua.

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
sumber
2
Persis apa yang saya cari. Jika daftar diurutkan berdasarkan kriteria terkait, ini jauh lebih masuk akal.
erich2k8
16

Anda mungkin ingin melihat scalex.org - ini memungkinkan Anda untuk mencari pustaka standar scala untuk fungsi dengan tanda tangannya. Misalnya, ketikkan yang berikut ini:

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

Anda akan melihat partisi .

oxbow_lakes
sumber
10
domain scalex.org saat ini mati. Tetapi ada alternatif - scala-search.org ;-).
monnef
1
Mengajari cara menangkap ikan!
PENGGUNA INI MEMBUTUHKAN BANTUAN
1
@monnef Ada alternatif untuk alternatif Anda untuk tahun 2020? :)
tehCivilian
14

Anda juga dapat menggunakan foldLeft jika Anda membutuhkan sesuatu yang lebih. Saya baru saja menulis beberapa kode seperti ini ketika partisi tidak memotongnya:

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.dll
sumber
1
Cara yang sangat bagus untuk menggunakan tuple dan foldLeft. Saya akhirnya menggunakan ListBuffer untuk secara efisien menyimpan dua daftar dalam urutan yang sama tetapi sebaliknya itu tepat untuk apa yang saya butuhkan.
Matt Hagopian
1

Saya tahu saya mungkin terlambat ke pesta dan ada jawaban yang lebih spesifik, tetapi Anda dapat memanfaatkannya 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)

Ini membuat kode Anda sedikit lebih tahan masa depan jika Anda perlu mengubah kondisi menjadi sesuatu yang bukan boolean.

Pembual
sumber
0

Jika Anda ingin membagi daftar menjadi lebih dari 2 bagian, dan mengabaikan batasannya, Anda dapat menggunakan sesuatu seperti ini (ubah jika Anda perlu mencari ints)

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
sumber