Convert a Scala list to a tuple?
Solution 1
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.
Solution 2
You can do it using scala extractors and pattern matching (link):
val x = List(1, 2, 3)
val t = x match {
case List(a, b, c) => (a, b, c)
}
Which returns a tuple
t: (Int, Int, Int) = (1,2,3)
Also, you can use a wildcard operator if not sure about a size of the List
val t = x match {
case List(a, b, c, _*) => (a, b, c)
}
Solution 3
an example using shapeless :
import shapeless._
import syntax.std.traversable._
val x = List(1, 2, 3)
val xHList = x.toHList[Int::Int::Int::HNil]
val t = xHList.get.tupled
Note: the compiler need some type informations to convert the List in the HList that the reason why you need to pass type informations to the toHList
method
Solution 4
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)
Solution 5
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.
![grautur](https://i.stack.imgur.com/k2iEi.png?s=256&g=1)
grautur
Updated on December 25, 2020Comments
-
grautur over 3 years
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? -
Jim Pivarski almost 10 yearsIn the context of this solution, the complaints about type-safety mean that you might get a MatchError at runtime. If you want, you can try-catch that error and do something if the List has the wrong length. Another nice feature of this solution is that the output doesn't have to be a tuple, it can be one of your own custom classes or some transformation of the numbers. I don't think you can do this with non-Lists, though (so you may need to do a .toList operation to the input).
-
Admin almost 10 yearsIs there some fixed-size list of elements with the same type? Not importing nonstandard libraries, like shapeless, of course.
-
Admin almost 10 yearsWould the resulting type of this hypothetical function be Any?
-
Tom Crockett almost 10 years@davips not that I know of, but it's easy enough to make your own, e.g.
case class Vec3[A](_1: A, _2: A, _3: A)
-
Admin almost 10 yearsThis would be a "tuple" of elements with the same type, I can't apply map on it, for instance.
-
Tom Crockett almost 10 years@davips as with any new data type you'd have to define how
map
etc works for it -
gdoubleod over 8 yearsThe case class limit has been lifted in 2.11 github.com/scala/scala/pull/2305
-
ely almost 6 yearsThis kind of limitation seems to be a resounding indicator of the uselessness of type safety more than anything else. It reminds me of the quote "the empty set has many interesting properties."
-
Peter L over 5 years@javadba I was looking for how to implement listToTuple() but ended up not needing to. The list syntactic sugar solved my problem.
-
Kolmar about 5 yearsThere is also another syntax, which in my opinion looks a bit better:
val List(c1, c2, c3) = myList
-
jwvh about 5 yearsWhen answering a question this old (over 6 years) it's often a good idea to point out how your answer is different from all the (12) older answers already submitted. In particular, your answer is only applicable if the length of the
List
/Tuple
is a known constant. -
Michael Olafisoye about 5 yearsThank you @ jwvh, will consider your comments going forward.
-
ig-dev about 5 yearsYou can do this with any type of Scala sequence using the +: syntax. Also, don't forget to add a catch-all
-
kfkhalili over 4 yearsThis does not appear to work for sizes larger than 22, at least for shapeless_2.11:2.3.3
-
Kolmar over 4 years@kfkhalili Yes, and it's not a shapeless limitation. Scala 2 itself doesn't allow tuples with more than 22 elements, so it's not possible to convert a List of a larger size to a Tuple using any method.
-
Anthony Awuley almost 3 yearsonly traits and abstract classes can have declared but undefined members. you have to make sure you test your solution before posting it.