Reputation: 41909
Given the function, f
, which, given a 2-tuple of Option[A]
's, puts non-empty tuple elements into an output List[A]
:
def f[A](xs: (Option[A], Option[A])): List[A] = xs match {
case (Some(x), Some(y)) => List(x, y)
case (None, Some(y)) => List(y)
case (Some(x), None) => List(x)
case (None, None) => List.empty
}
How can I write a generic f
, i.e. fGen
, that will handle any tuple size, i.e. 2 to N?
Perhaps I can use shapeless?
Upvotes: 4
Views: 181
Reputation: 1644
Here's a variant of @Eastsun's solution which doesn't require a type parameter. Thanks to @Eastsun for pointing out that list.map(ev)
can be used instead of a cast.
def tuple2list[P <: Product, L <: HList, Lub, A](p: P)(
implicit gen: Generic.Aux[P, L],
toList: ToList[L, Lub],
ev: Lub <:< Option[A]
): List[A] =
gen.to(p).toList.map(ev).flatten
val xs = (Option(1), None, Option(2))
println(tuple2list(xs)) // List(1, 2)
Alternatively you can simply transform the Product
to a List
and apply flatten
afterwards:
def tuple2list[P <: Product, L <: HList, Lub](p: P)(
implicit gen: Generic.Aux[P, L],
toList: ToList[L, Lub]
): List[Lub] =
gen.to(p).toList
val xs = (Option(1), None, Option(2))
println(tuple2list(xs).flatten) // List(1, 2)
Upvotes: 2
Reputation: 18859
Here is my (not perfect) solution:
@ object Tuple2List {
import shapeless._
import syntax.std.tuple._
import ops.hlist.ToTraversable
trait Imp[A] {
def apply[P <: Product, L <: HList](p: P)
(implicit gen: Generic.Aux[P, L],
lub: LUBConstraint[L, Option[A]],
trav: ToTraversable.Aux[L, List, Option[A]]): List[A] =
gen.to(p).toList.flatMap(_.toList)
}
def apply[A] = new Imp[A]{ }
}
defined object Tuple2List
@ val xs = (Option(1), None, Option(2))
xs: (Option[Int], None.type, Option[Int]) = (Some(1), None, Some(2))
@ Tuple2List[Int](xs)
res9: List[Int] = List(1, 2)
@ val ys = (Option(1), None, Option(2), None, Option(3))
ys: (Option[Int], None.type, Option[Int], None.type, Option[Int]) = (Some(1), None, Some(2), None, Some(3))
@ Tuple2List[Int](ys)
res11: List[Int] = List(1, 2, 3)
Note that you need to pass type parameter A
to make the scala compiler happy.
Upvotes: 3
Reputation: 268
The following code works with caveats:
can improve on
f
type safe (due to type erasure limited to Option
)asInstanceOf
/**
* param xs: Tuple of variable size (upto 22)
*/
def f[A](xs: Product): List[A] = xs.productIterator.toList.flatMap(_.asInstanceOf[Option[A]])
val tuple = (Some(1), Some(2), None, Some(3))
f[Int](tuple) // List(1, 2, 3)
Upvotes: 1