【问题标题】:In Scala, is there an easy way to convert a case class into a tuple?在 Scala 中,是否有一种简单的方法可以将案例类转换为元组?
【发布时间】:2011-12-26 15:23:14
【问题描述】:

有没有一种简单的方法可以将案例类转换为元组?

当然,我可以轻松编写样板代码来执行此操作,但我的意思是没有样板代码。

我真正追求的是一种轻松使案例类按字典顺序排列的方法。我可以通过导入 scala.math.Ordering.Implicits._ 来实现元组的目标,瞧,我的元组为它们定义了一个排序。但是 scala.math.Ordering 中的隐含通常不适用于案例类。

【问题讨论】:

    标签: scala tuples case-class


    【解决方案1】:

    在伴生对象中调用unapply().get怎么样?

    case class Foo(foo: String, bar: Int)
    
    val (str, in) = Foo.unapply(Foo("test", 123)).get
    // str: String = test
    // in: Int = 123
    

    【讨论】:

    • 感谢您的提示,在 REPL 2.9.0-1 中,我必须删除 () 才能“获取”
    • 酷,成功了!根据您的建议,我能够像这样订购我的案例课程:val thisTuple: Ordered[(String, Int)] = Foo.unapply(this).get; val thatTuple = Foo.unapply(that).get; thisTuple compare thatTuple。这不是世界上最漂亮的东西,所以我仍然愿意接受建议,但你的回答肯定能完成工作。谢谢!
    • 这很麻烦,与仅仅进行样板化相比并没有真正的改进。
    • 在新的 scala 版本中不能调用 get(),只能调用 get
    • 这里.unapply 的方法契约保证Option 将是Some - 在其他情况下它将是None。在其他情况下,我们可能不知道,但在这里我们知道,并且不必在运行时验证此合约。因此可以拨打.get
    【解决方案2】:

    Shapeless 会为你做这件事。

      import shapeless._
      import shapeless.syntax.std.product._
    
      case class Fnord(a: Int, b: String)
    
      List(Fnord(1, "z - last"), Fnord(1, "a - first")).sortBy(_.productElements.tupled)
    

    获取

    res0: List[Fnord] = List(Fnord(1,a - first), Fnord(1,z - last))
    

    productElements 将案例类转换为无形 HList:

    scala> Fnord(1, "z - last").productElements
    res1: Int :: String :: shapeless.HNil = 1 :: z - last :: HNil
    

    HLists 用#tupled 转换成元组:

    scala> Fnord(1, "z - last").productElements.tupled
    res2: (Int, String) = (1,z - last)
    

    性能可能很糟糕,因为您一直在转换。您可能会将所有内容转换为元组形式,对其进行排序,然后使用(Fnord.apply _).tupled 之类的内容将其转换回来。

    【讨论】:

      【解决方案3】:

      您可以尝试扩展 ProductN 特征,对于 N=1-22,TupleN 扩展。它会给你很多元组语义,比如_1_2等方法。根据您使用类型的方式,这可能就足够了,而无需创建实际的元组。

      【讨论】:

      • 我不确定如何让这种方法为我工作。我希望我的案例类要么按字典顺序排列,要么按字典顺序排列,而不必编写大量样板文件。扩展 ProductN 似乎不适用于 scala.math.Ordering 中的隐式,它允许人们轻松地为元组提供字典顺序。
      【解决方案4】:

      在尝试做同样的事情时遇到了这个旧线程。我最终确定了这个解决方案:

      case class Foo(foo: String, bar: Int)
      
      val testFoo = Foo("a string", 1)
      
      val (str, in) = testFoo match { case Foo(f, b) => (f, b) }
      

      【讨论】:

      • 我喜欢这种方法,因为我可以避免在从unapply 返回的Option 上调用.get。我想知道为什么案例类没有一个名为 .toTuple 的方法,它返回文字元组,而不是在 Option 中;即你实际上在做什么。对于案例类实例上的 .unapply,结果将始终是“Some”,迫使我调用 .get,这是代码异味。
      • “当然,我可以轻松编写样板代码来做到这一点,但我的意思是没有样板代码” - 这个答案不符合该标准,实际上是比简单的(testFoo.foo, testFoo.bar)更长的时间@
      【解决方案5】:

      Scala 3 对案例类和元组之间的转换具有一流的支持:

      scala> case class Foo(foo: String, bar: Int)
      // defined case class Foo
      
      scala> Tuple.fromProductTyped(Foo("a string", 1))
      val res0: (String, Int) = (a string,1)
      
      scala> summon[deriving.Mirror.ProductOf[Foo]].fromProduct(res0)
      val res1: deriving.Mirror.ProductOf[Foo]#MirroredMonoType = Foo(a string,1)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-06
        • 2016-06-05
        • 2022-09-24
        • 2023-02-24
        • 1970-01-01
        • 2011-01-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多