16

Sometimes there are needs to create tuples from small collections(for example scalding framework).

def toTuple(list:List[Any]):scala.Product = ... 

6 Answers 6

15

You really don't want your method to return Product since this is uselessly vague. If you want to be able to use the returned object as a tuple, then you'll have to know its arity. So what you can do is have a series of toTupleN methods for different arities. For convenience, you can add these as implicit methods on Seq.

How about this:

class EnrichedWithToTuple[A](elements: Seq[A]) { def toTuple2 = elements match { case Seq(a, b) => (a, b) } def toTuple3 = elements match { case Seq(a, b, c) => (a, b, c) } def toTuple4 = elements match { case Seq(a, b, c, d) => (a, b, c, d) } def toTuple5 = elements match { case Seq(a, b, c, d, e) => (a, b, c, d, e) } } implicit def enrichWithToTuple[A](elements: Seq[A]) = new EnrichedWithToTuple(elements) 

and use it like:

scala> List(1,2,3).toTuple3 res0: (Int, Int, Int) = (1,2,3) 
Sign up to request clarification or add additional context in comments.

1 Comment

Alternately, if you are expecting tuples of a single arity, you can just write out exampleList match { case Seq(a, b, c) => (a, b, c)}
14

If, as @dhg observed, you know the expected arity up front you can do something useful here. Using shapeless you could write,

scala> import shapeless._ import shapeless._ scala> import Traversables._ import Traversables._ scala> import Tuples._ import Tuples._ scala> List(1, 2, 3).toHList[Int :: Int :: Int :: HNil] map tupled res0: Option[(Int, Int, Int)] = Some((1,2,3)) 

1 Comment

But Shapeless doesn't provide the way to create a Tuple from List if length of list is undefined in compile time, it can lead to runtime errors
12

If you don't know the arity up front and want to do a terrible terrible hack, you can do this:

def toTuple[A <: Object](as:List[A]):Product = { val tupleClass = Class.forName("scala.Tuple" + as.size) tupleClass.getConstructors.apply(0).newInstance(as:_*).asInstanceOf[Product] } toTuple: [A <: java.lang.Object](as: List[A])Product scala> toTuple(List("hello", "world")) res15: Product = (hello,world) 

2 Comments

+1 Very useful for cleaning up assignment from strings. It doesn't work for var (a, b, c) = toTuple(myIter.toList) - Any ideas?
Rubistro: for that purpose you can use var List(a,b,c) = myIter.toList
5

Do you want a Tuple or just a Product. Because for the latter:

case class SeqProduct[A](elems: A*) { override def productArity: Int = elems.size override def productElement(i: Int) = elems(i) } SeqProduct(List(1, 2, 3): _*) 

1 Comment

Also wishing for Tuple, so I could use FunctionX.tupled, which indeed requires a TupleX, not Product (note even ProductX).
1

Based on @Kim Stebel's idea, I wrote a simple utility that creates tuple from seq.

import java.lang.reflect.Constructor /** * Created by Bowen Cai on 1/24/2015. */ sealed trait Product0 extends Any with Product { def productArity = 0 def productElement(n: Int) = throw new IllegalStateException("No element") def canEqual(that: Any) = false } object Tuple0 extends Product0 { override def toString() = "()" } case class SeqProduct(elems: Any*) extends Product { override def productArity: Int = elems.size override def productElement(i: Int) = elems(i) override def toString() = elems.addString(new StringBuilder(elems.size * 8 + 10), "(" , ",", ")").toString() } object Tuples { private[this] val ctors = { val ab = Array.newBuilder[Constructor[_]] for (i <- 1 to 22) { val tupleClass = Class.forName("scala.Tuple" + i) ab += tupleClass.getConstructors.apply(0) } ab.result() } def toTuple(elems: Seq[AnyRef]): Product = elems.length match { case 0 => Tuple0 case size if size <= 22 => ctors(size - 1).newInstance(elems: _*).asInstanceOf[Product] case size if size > 22 => new SeqProduct(elems: _*) } } 

1 Comment

Thank you, @xKommando. This is just what I needed. Because I wanted toTuple to apply to a sequence of Any rather than AnyRef, I replaced the type of elems with Seq[Any] and replaced the expression for the case size<=22 with val refs = for (e <- elems) yield e.asInstanceOf[AnyRef] ctors(size - 1).newInstance(refs: _*).asInstanceOf[Product] (based on the answer here: [link] (stackoverflow.com/questions/16751484/…))
0
scala> val numbers = Seq(1,2,4) numbers: Seq[Int] = List(1, 2, 4) scala> val string = numbers.mkString("(",",",")") string: String = (1,2,4) *** mkString(start:String, sep: String, end: String) 

I have generated it in my where-in-clause.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.