วิธีแบ่งลำดับออกเป็นสองส่วนโดยเพรดิเคต?


120

ฉันจะแบ่งลำดับออกเป็นสองรายการโดยเพรดิเคตได้อย่างไร

ทางเลือก: ฉันสามารถใช้filterและfilterNotหรือเขียนวิธีการของตัวเองได้ แต่ไม่มีวิธีการทั่วไปที่ดีกว่านี้ (ในตัว) หรือไม่?

คำตอบ:


194

โดยใช้partitionวิธีการ:

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)เป็นวิธีทำลายทูเพิลที่เป็นผลลัพธ์partitionด้วยวิธีที่อ่านได้
k0pernikus

2
_ % 2 == 0หนึ่งสามารถร่นฟังก์ชั่นภายในพาร์ทิชัน
k0pernikus

138

ดีที่partitionเป็นสิ่งที่คุณต้องการ - spanมีวิธีการอื่นที่ยังใช้คำกริยาที่จะแยกรายการในที่สอง:

อันแรกพาร์ติชันจะใส่องค์ประกอบ "จริง" ทั้งหมดไว้ในรายการเดียวและอื่น ๆ ในรายการที่สอง

spanจะใส่องค์ประกอบทั้งหมดในรายการเดียวจนกว่าองค์ประกอบจะเป็น "เท็จ" (ในรูปของเพรดิเคต) จากจุดนั้นไปข้างหน้ามันจะใส่องค์ประกอบในรายการที่สอง

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

2
สิ่งที่ฉันกำลังมองหา เมื่อรายการเรียงลำดับตามเกณฑ์ที่เกี่ยวข้องสิ่งนี้จะสมเหตุสมผลกว่ามาก
erich2k8

16

คุณอาจต้องการดูที่scalex.orgซึ่งจะช่วยให้คุณค้นหาไลบรารีมาตรฐานของสกาล่าเพื่อหาฟังก์ชันโดยใช้ลายเซ็น ตัวอย่างเช่นพิมพ์ดังต่อไปนี้:

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

คุณจะเห็นพาร์ทิชัน


10
ในขณะนี้โดเมน scalex.org หยุดทำงานแล้ว แต่มีทางเลือกอื่น - scala-search.org ;-)
monnef

1
สอนวิธีจับปลา!
นี้ต้องการความช่วยเหลือ

1
@monnef ทางเลือกอื่นสำหรับทางเลือกของคุณในปี 2020 หรือไม่? :)
tehCivilian

14

คุณยังสามารถใช้ foldLeft ได้หากคุณต้องการอะไรเพิ่มเติมเล็กน้อย ฉันเพิ่งเขียนโค้ดแบบนี้เมื่อพาร์ติชันไม่ได้ตัดมัน:

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
วิธีที่ดีมากในการใช้ tuple และ foldLeft ฉันลงเอยด้วยการใช้ ListBuffer เพื่อให้ทั้งสองรายการอยู่ในลำดับเดียวกันอย่างมีประสิทธิภาพ แต่อย่างอื่นมันก็ตรงกับสิ่งที่ฉันต้องการ
Matt Hagopian

1

ฉันรู้ว่าฉันอาจจะไปปาร์ตี้ช้าและมีคำตอบที่เฉพาะเจาะจงมากกว่านี้ แต่คุณสามารถใช้ประโยชน์ได้ 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)

สิ่งนี้ทำให้โค้ดของคุณสามารถพิสูจน์อนาคตได้มากขึ้นหากคุณต้องการเปลี่ยนเงื่อนไขเป็นสิ่งที่ไม่ใช่บูลีน


0

หากคุณต้องการแยกรายการออกเป็นมากกว่า 2 ชิ้นและไม่สนใจขอบเขตคุณสามารถใช้สิ่งนี้ได้ (แก้ไขหากคุณต้องการค้นหา 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"})
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.