Como dividir uma sequência em duas partes por predicado?

120

Como faço para dividir uma sequência em duas listas por um predicado?

Alternativa: Eu posso usar filtere filterNot, ou escrever meu próprio método, mas não existe um método mais geral (integrado) melhor?

John Threepwood
fonte

Respostas:

194

Usando o partitionmétodo:

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
fonte
1
val (even, odd) = List(1,2,3,4).partition(x => x % 2 == 0)é uma maneira de destruir a tupla resultante de partitionde uma forma legível.
k0pernikus
2
Pode-se encurtar a função dentro da partição para _ % 2 == 0.
k0pernikus
138

Bem que partitionfoi a coisa que você queria - não há outro método que também utiliza um predicado para dividir uma lista em duas: span.

O primeiro, partição , colocará todos os elementos "verdadeiros" em uma lista e os outros na segunda lista.

span irá colocar todos os elementos em uma lista até que um elemento seja "falso" (em termos do predicado). Desse ponto em diante, ele colocará os elementos da segunda lista.

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
fonte
2
Exatamente o que eu estava procurando. Quando a lista é ordenada por um critério relacionado, isso faz muito mais sentido.
erich2k8
16

Você pode querer dar uma olhada em scalex.org - ele permite que você pesquise na biblioteca padrão do scala por funções por sua assinatura. Por exemplo, digite o seguinte:

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

Você veria partição .

oxbow_lakes
fonte
10
O domínio scalex.org está morto. Mas existe uma alternativa - scala-search.org ;-).
Monnef
1
Ensinando como pegar um peixe!
ESTE USUÁRIO PRECISA DE AJUDA
1
@monnef Alguma alternativa para sua alternativa para 2020? :)
tehCivilian
14

Você também pode usar o foldLeft se precisar de algo um pouco mais. Acabei de escrever um código como este quando a partição não o cortou:

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
fonte
1
Uma maneira muito boa de usar uma tupla e foldLeft. Acabei usando um ListBuffer para manter as duas listas na mesma ordem de forma eficiente, mas, fora isso, estava certo para o que eu precisava.
Matt Hagopian
1

Sei que posso chegar atrasado para a festa e há respostas mais específicas, mas você poderia fazer bom uso de 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)

Isso torna seu código um pouco mais preparado para o futuro se você precisar alterar a condição para algo não booleano.

Gabber
fonte
0

Se você quiser dividir uma lista em mais de 2 partes e ignorar os limites, pode usar algo como isto (modifique se precisar pesquisar por 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
fonte