Convert a Scala list to a tuple?

71,124

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.

Share:
71,124
grautur
Author by

grautur

Updated on December 25, 2020

Comments

  • grautur
    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
    Jim Pivarski almost 10 years
    In 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
    Admin almost 10 years
    Is there some fixed-size list of elements with the same type? Not importing nonstandard libraries, like shapeless, of course.
  • Admin
    Admin almost 10 years
    Would the resulting type of this hypothetical function be Any?
  • Tom Crockett
    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
    Admin almost 10 years
    This would be a "tuple" of elements with the same type, I can't apply map on it, for instance.
  • Tom Crockett
    Tom Crockett almost 10 years
    @davips as with any new data type you'd have to define how map etc works for it
  • gdoubleod
    gdoubleod over 8 years
    The case class limit has been lifted in 2.11 github.com/scala/scala/pull/2305
  • ely
    ely almost 6 years
    This 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
    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
    Kolmar about 5 years
    There is also another syntax, which in my opinion looks a bit better: val List(c1, c2, c3) = myList
  • jwvh
    jwvh about 5 years
    When 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
    Michael Olafisoye about 5 years
    Thank you @ jwvh, will consider your comments going forward.
  • ig-dev
    ig-dev about 5 years
    You can do this with any type of Scala sequence using the +: syntax. Also, don't forget to add a catch-all
  • kfkhalili
    kfkhalili over 4 years
    This does not appear to work for sizes larger than 22, at least for shapeless_2.11:2.3.3
  • Kolmar
    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
    Anthony Awuley almost 3 years
    only traits and abstract classes can have declared but undefined members. you have to make sure you test your solution before posting it.