Как разбить последовательность на две части по предикату?


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.

Первый, partition поместит все «истинные» элементы в один список, а остальные - во второй.

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 - он позволяет вам искать в стандартной библиотеке scala функции по их сигнатуре. Например, введите следующее:

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

Вы увидите раздел .


10
scalex.org в настоящее время мертв. Но есть альтернатива - scala-search.org ;-).
monnef


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
Очень хороший способ использовать кортеж и foldLeft. В итоге я использовал ListBuffer, чтобы эффективно хранить два списка в одном порядке, но в остальном это было то, что мне нужно.
Мэтт Хагопиан

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"})
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.