Convert a Scala list to a tuple?

2019-01-02 20:27发布

How can I convert a list with (say) 3 elements into a tuple of size 3?

For example, let's say I have val x = List(1, 2, 3) and I want to convert this into (1, 2, 3). How can I do this?

12条回答
牵手、夕阳
2楼-- · 2019-01-02 21:17

FWIW, I wanted a tuple to initalise a number of fields and wanted to use the syntactic sugar of tuple assignment. EG:

val (c1, c2, c3) = listToTuple(myList)

It turns out that there is syntactic sugar for assigning the contents of a list too...

val c1 :: c2 :: c3 :: Nil = myList

So no need for tuples if you've got the same problem.

查看更多
泛滥B
3楼-- · 2019-01-02 21:17

This can also be done in shapeless with less boilerplate using Sized:

scala> import shapeless._
scala> import shapeless.syntax.sized._

scala> val x = List(1, 2, 3)
x: List[Int] = List(1, 2, 3)

scala> x.sized(3).map(_.tupled)
res1: Option[(Int, Int, Int)] = Some((1,2,3))

It's type-safe: you get None, if the tuple size is incorrect, but the tuple size must be a literal or final val (to be convertible to shapeless.Nat).

查看更多
其实,你不懂
4楼-- · 2019-01-02 21:19

as far as you have the type:

val x: List[Int] = List(1, 2, 3)

def doSomething(a:Int *)

doSomething(x:_*)
查看更多
忆尘夕之涩
5楼-- · 2019-01-02 21:21

You can't do this in a typesafe way. Why? Because in general we can't know the length of a list until runtime. But the "length" of a tuple must be encoded in its type, and hence known at compile time. For example, (1,'a',true) has the type (Int, Char, Boolean), which is sugar for Tuple3[Int, Char, Boolean]. The reason tuples have this restriction is that they need to be able to handle a non-homogeneous types.

查看更多
荒废的爱情
6楼-- · 2019-01-02 21:22

If you are very sure that your list.size<23 use it:

def listToTuple[A <: Object](list:List[A]):Product = {
  val class = Class.forName("scala.Tuple" + list.size)
  class.getConstructors.apply(0).newInstance(list:_*).asInstanceOf[Product]
}
listToTuple: [A <: java.lang.Object](list: List[A])Product

scala> listToTuple(List("Scala", "Smart"))
res15: Product = (Scala,Smart)
查看更多
牵手、夕阳
7楼-- · 2019-01-02 21:27

Shapeless 2.0 changed some syntax. Here's the updated solution using shapeless.

import shapeless._
import HList._
import syntax.std.traversable._

val x = List(1, 2, 3)
val y = x.toHList[Int::Int::Int::HNil]
val z = y.get.tupled

The main issue being that the type for .toHList has to be specified ahead of time. More generally, since tuples are limited in their arity, the design of your software might be better served by a different solution.

Still, if you are creating a list statically, consider a solution like this one, also using shapeless. Here, we create an HList directly and the type is available at compile time. Remember that an HList has features from both List and Tuple types. i.e. it can have elements with different types like a Tuple and can be mapped over among other operations like standard collections. HLists take a little while to get used to though so tread slowly if you are new.

scala> import shapeless._
import shapeless._

scala> import HList._
import HList._

scala>   val hlist = "z" :: 6 :: "b" :: true :: HNil
hlist: shapeless.::[String,shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.HNil]]]] = z :: 6 :: b :: true :: HNil

scala>   val tup = hlist.tupled
tup: (String, Int, String, Boolean) = (z,6,b,true)

scala> tup
res0: (String, Int, String, Boolean) = (z,6,b,true)
查看更多
登录 后发表回答