24

I am trying to slice a tuple, removing the last two items. I tried using the list drop/take methods but I can't succeed to get a tuple back.

Here is the approach I tried :

scala> val myTuple = (1, 2, 4, 5, 0, 5)
myTuple: (Int, Int, Int, Int, Int, Int) = (1,2,4,5,0,5)

scala> val myList = myTuple.productIterator.toList
myList: List[Any] = List(1, 2, 4, 5, 0, 5)

scala> val mySubList = myList.dropRight(2)
mySubList: List[Any] = List(1, 2, 4, 5)

scala> val mySubTuple = ???

I saw here that list to tuple isn't (yet?) possible in scala.

Are there other ways to get that subtuple (without dealing with myTuple._1, myTuple._2...) ?

Community
  • 1
  • 1
kheraud
  • 5,048
  • 7
  • 46
  • 75
  • 4
    I think this can be done with [shapeless](https://github.com/milessabin/shapeless) `HList`. Take a look at [this question](http://stackoverflow.com/questions/9028459/a-clean-way-to-combine-two-tuples-into-a-new-larger-tuple-in-scala). – incrop Jun 19 '12 at 09:54

3 Answers3

42

This is the sort of thing that shapeless can do in a generic way, involving conversion into an HList.

First - get shapeless. Then run scala with dependent method types switched on (on by default in 2.10):

C:\Scala\sdk\scala-2.9.2\bin>scala -Ydependent-method-types
Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_04).
Type in expressions to have them evaluated.
Type :help for more information.

Add shapeless to the classpath:

scala> :cp C:\Users\cmarsha\Downloads\shapeless_2.9.2-1.2.2.jar
Added 'C:\Users\cmarsha\Downloads\shapeless_2.9.2-1.2.2.jar'.  Your new classpath is:
"C:\tibco\tibrv\8.2\lib\tibrvnative.jar;C:\Users\cmarsha\Downloads\shapeless_2.9.2-1.2.2.jar"

Now let us play!

scala> (1, 2.3, 'a, 'b', "c", true)
res0: (Int, Double, Symbol, Char, java.lang.String, Boolean) = (1,2.3,'a,b,c,true)

We must import shapeless

scala> import shapeless._; import Tuples._; import Nat._
import shapeless._
import Tuples._
import Nat._

We turn our tuple into an HList

scala> res0.hlisted
res2: shapeless.::[Int,shapeless.::[Double,shapeless.::[Symbol,shapeless.::[Char,shapeless.::[java.lang.String,shapeless.::[Boolean,shapeless.HNil]]]]]] = 1 :: 2.3 :: 'a :: b :: c :: true :: HNil

Then we take the first 4 (notice that _4 is a type parameter, not a method argument)

scala> res2.take[_4]
res4: shapeless.::[Int,shapeless.::[Double,shapeless.::[Symbol,shapeless.::[Char, shapeless.HNil]]]] = 1 :: 2.3 :: 'a :: b :: HNil

Now convert back to a tuple

scala> res4.tupled
res5: (Int, Double, Symbol, Char) = (1,2.3,'a,b)

We could shorten this:

val (a, b, c, d) = sixtuple.hlisted.take[_4].tupled 
//a, b, c and d would all have the correct inferred type

This of course generalizes to the first M elements of an N-tuple

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • 4
    Be sure to include `-Ydependent-method-types` or you'll get `could not find implicit value for parameter tupler: shapeless.Tupler[take.Out]` (confused me for a while trying to come up with an answer to this) – Brian McKenna Jun 19 '12 at 12:17
  • Awesome answer, and in the afternoon ! (French timezone :) ) – kheraud Jun 19 '12 at 12:49
  • 1
    How did this change in shapeless 2.00? Can't get it to compile – Edmondo May 16 '15 at 10:23
5
scala> val myTuple = (1, 2, 4, 5, 0, 5)
myTuple: (Int, Int, Int, Int, Int, Int) = (1,2,4,5,0,5)

scala> myTuple match {
     |   case (a, b, c, d, _, _) => (a, b, c, d)
     | }
res0: (Int, Int, Int, Int) = (1,2,4,5)
senia
  • 37,745
  • 4
  • 88
  • 129
  • It works but requires to explicitly write the pattern matching. I am looking for a way to do that no matter the length of the tuple. – kheraud Jun 19 '12 at 11:42
  • See my answer - I have a fully worked generalized example using Miles Sabin's shapeless library – oxbow_lakes Jun 19 '12 at 12:14
5

how about:


scala> val myTuple = (1,2,4,5,0,5)
myTuple: (Int, Int, Int, Int, Int, Int) = (1,2,4,5,0,5)

scala> val (left,right):Tuple2[List[Int],List[Int]] = myTuple.productIterator.toList.splitAt(myTuple.productArity - 2)
left: List[Int] = List(1, 2, 4, 5)
right: List[Int] = List(0, 5)

scala> val mytuple2 = (right(0),right(1))
mytuple2: (Int, Int) = (0,5)


nairbv
  • 4,045
  • 1
  • 24
  • 26