Is there way to create tuple from list(without codegeneration)?

后端 未结 6 2179
[愿得一人]
[愿得一人] 2020-11-27 17:44

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

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

相关标签:
6条回答
  • 2020-11-27 18:11

    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))
    
    0 讨论(0)
  • 2020-11-27 18:14

    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)
    
    0 讨论(0)
  • 2020-11-27 18:21
    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.

    0 讨论(0)
  • 2020-11-27 18:22

    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): _*)
    
    0 讨论(0)
  • 2020-11-27 18:23

    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: _*)
      }
    
    }
    
    0 讨论(0)
  • 2020-11-27 18:26

    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)
    
    0 讨论(0)
提交回复
热议问题