Как мне преобразовать кортеж опций в кортеж опций в Scala?

Этот вопрос противоположен этот вопрос.

val x = Some((1, 2))
val (y: Option[Int], z: Option[Int]) = ???

Полезны как чистые ответы Scala, так и ответы Scalaz.


person Jim Hunziker    schedule 30.01.2014    source источник
comment
Интересно, дает ли Shapeless (github.com/milessabin/shapeless) краткий ответ на этот вопрос?   -  person Randall Schulz    schedule 30.01.2014
comment
Вы можете получить итератор с помощью tuple.productIterator. Также есть productArity и productElement(n: Int)   -  person Millie Smith    schedule 30.01.2014


Ответы (5)


Я действительно думаю, что ваш ответ совершенно ясен, но, поскольку вы упомянули Scalaz, эта операция называется unzip:

scala> import scalaz._, std.option._
import scalaz._
import std.option._

scala> val x: Option[(Int, Int)] = Some((1, 2))
x: Option[(Int, Int)] = Some((1,2))

scala> Unzip[Option].unzip(x)
res0: (Option[Int], Option[Int]) = (Some(1),Some(2))

Вы должны уметь писать просто x.unzip, но, к сожалению, ужасное неявное преобразование стандартной библиотеки из Option в Iterable сработает первым, и в итоге вы получите (Iterable[Int], Iterable[Int]).


Оглядываясь назад год спустя: на самом деле это возможно сделать с UnzipPairOps от Scalaz:

scala> import scalaz.std.option._, scalaz.syntax.unzip._
import scalaz.std.option._
import scalaz.syntax.unzip._

scala> val x: Option[(Int, Int)] = Some((1, 2))
x: Option[(Int, Int)] = Some((1,2))

scala> x.unfzip
res0: (Option[Int], Option[Int]) = (Some(1),Some(2))

О чем ты думал, 2014 меня?

person Travis Brown    schedule 30.01.2014

Согласно ответу Джима, но с синтаксисом, который некоторым может быть легче читать:

val x = Some(1 -> 2)
val (y, z) = x map {case (a,b) => Some(a) -> Some(b)} getOrElse (None -> None)
person Kevin Wright    schedule 30.01.2014

Лучшее, что я мог придумать, это следующее, но мне это кажется глупым:

val x = Some((1, 2))
val (y, z) = x map {x => (Some(x._1), Some(x._2)) } getOrElse (None, None)
person Jim Hunziker    schedule 30.01.2014
comment
Я не думаю, что это глупо, это описывает то, что вы пытаетесь сделать, я мог бы просто заменить x map {x => ... на x map {case (fst, snd) => ..., чтобы не вызывать _1 и _2. - person vptheron; 30.01.2014

Начиная с версии Scala 2.13, именно это поведение обеспечивается в стандартной библиотеке Option#unzip:

// val x: Option[(Int, String)] = Some(1, "hello")
x.unzip
// (Some(1), Some("hello"))
val (y, z) = x.unzip
// y: Option[Int] = Some(1)
// z: Option[String] = Some("hello")

// val x: Option[(Int, String)] = None
x.unzip
// (None, None)

Также обратите внимание на эквивалент для трехэлементных кортежей: Option#unzip3.

person Xavier Guihot    schedule 08.02.2019

Как предложил Роб Норрис на канале кошек gitter, вы можете сделать это с кошками, вызвав .separate:

@ import cats.implicits._
import cats.implicits._

@ val x: Option[(Int, Int)] = Some((1, 2))
x: Option[(Int, Int)] = Some((1, 2))

@ x.separate
res6: (Option[Int], Option[Int]) = (Some(1), Some(2))

Аннотация типа в присвоении x актуальна; неявное работает с Option, а не с Some:

@ val x = Some((1, 2))
x: Some[(Int, Int)] = Some((1, 2))

@ x.separate
cmd2.sc:1: value separate is not a member of Some[(Int, Int)]
val res2 = x.separate

separate предоставлено cats.MonadCombine

person Mark T.    schedule 08.05.2018
comment
Вы можете использовать cats.syntax.option._, чтобы избавиться от аннотаций типов. (1, 2).some будет восприниматься как Option[(Int, Int)]. - person Mikhail Chugunkov; 17.10.2018