Scala像javascript对象一样解构

时间:2018-03-13 19:18:21

标签: javascript scala

我希望javascripts能够在scala中解构。考虑js中的对象和scala中的case类。如果我只想要来自object / case类的第一个和最后一个属性,那么在scala中我需要列出所有属性或者我得到编译错误。

在scala案例类解构(据我所知)是使用unapply方法的位置

case class ABC(a: Int, b: Int, c: Int)
val obj = ABC(1,2,3)
// deconstruction
val (a, _, c) = obj

在javascript中按属性名称

const obj = { a: 1, b: 2, c: 3 }
const { a, c } = obj
// or
const { a: attrA, c: attrC } = obj
// scala deconstruction is essentially javascript array deconstruction
const arr = [1,2,3]
const [ first, _, last ] = arr

是否可以在scala中使用'javascript like'对象解构?在这个例子中,差异是微不足道的,但是在未来的修订中可能会改变顺序的更多属性,scala代码变得更难管理。

3 个答案:

答案 0 :(得分:4)

如果我错了,请纠正我,但据我了解,

obj.a

只需将名称obj.ca下的值cimport纳入范围。如果模式匹配过于冗长,您可以使用case class Abc(a: Int, b: Int, c: Int) val obj = Abc(1, 2, 3) val somethingUsingOnlyAandC = { import obj.{a, c} a + c } println(somethingUsingOnlyAandC) // Output: 4 轻松实现类似的效果,例如:

case class Abc(a: Int, b: Int, c: Int)

val x = Abc(1, 2, 3)
val y = Abc(4, 5, 6)

val bar = {
  import x.{a => a1, c => c1}
  import y.{a => a2, c => c2}
  a1 * a2 + c1 * c2
}

println(bar) // prints "22", 1 * 4 + 3 * 6

如果您有多个具有重命名导入的同一类对象,也可以使用它:

CONNECT

答案 1 :(得分:1)

如果您坚持使用提取器,则可以使用构思from this answer

object && {
  def unapply[A](a: A): Option[(A, A)] = Some((a, a))
}

并为每个成员变量另外定义一个单独的提取器:

case class Abc(a: Int, b: Int, c: Int)

object Abc {
  object A { def unapply(x: Abc): Option[Int] = Some(x.a) }
  object B { def unapply(x: Abc): Option[Int] = Some(x.b) }
  object C { def unapply(x: Abc): Option[Int] = Some(x.c) }
}

现在你可以在像这样的模式匹配中使用它:

val z = Abc(42, 100, 58)
import Abc._
val A(a) && B(b) = z
println(s"a = $a , b = $b")

此示例的输出为:

a = 42 , b = 100

请注意,它适用于与&&链接在一起的任意数量的提取器,例如:

case class Abc(a1: Int, a2: Int, a3: Int, a4: Int, a5: Int, a6: Int, a7: Int, a8: Int)

object Abc {
  object A1 { def unapply(x: Abc): Option[Int] = Some(x.a1) }
  object A2 { def unapply(x: Abc): Option[Int] = Some(x.a2) }
  object A3 { def unapply(x: Abc): Option[Int] = Some(x.a3) }
  object A4 { def unapply(x: Abc): Option[Int] = Some(x.a4) }
  object A5 { def unapply(x: Abc): Option[Int] = Some(x.a5) }
  object A6 { def unapply(x: Abc): Option[Int] = Some(x.a6) }
  object A7 { def unapply(x: Abc): Option[Int] = Some(x.a7) }
  object A8 { def unapply(x: Abc): Option[Int] = Some(x.a8) }

}

object && {
  def unapply[A](a: A): Option[(A, A)] = Some((a, a))
}

val z = Abc(1, 2, 3, 4, 5, 6, 7, 8)
import Abc._
val A3(a3) && A5(a5) && A7(a7) = z
println(s"$a3 $a5 $a7")

输出

3 5 7

正如所料。如果你真的想经常使用这些笨重的对象,你应该考虑为样板代码生成代码。

答案 2 :(得分:0)

您可以解构提供unapply method (extractor)的所有内容:

class Test(val a: String, val b: Int)

object Test {

  def unapply(test: Test): Option[(String, Int)] = Some((test.a, test.b))
}

val test = new Test("test", 0)
val (a, b) = test

它是为案例类自动生成的,因此您的示例也允许它:

case class ABC(a: Int, b: Int, c: Int)
val obj = ABC(1,2,3)
// deconstruction
val ABC(a, _, c) = obj

请注意,有一个包含提取器的对象名称 - 它告诉编译器在哪里搜索它(如果你想要的话可​​能有几个提取器!)。

相同的机制用于模式匹配,因此当您使用:

option match {
  case Some(value) => // uses Some.unapply(option)
  case None        => // uses None.unapply(option)
}

either match {
  case Left(left)   => // uses Left.unapply(either)
  case Right(right) => // uses Right.unapply(either)
}

提取器获取带有内容的元组的Option - 有些意味着匹配成功,而无意味着失败。

对于集合,您可能需要查找确切示例,具体取决于您的数学运算:

list match {
  case v1 :: v2 :: Nil => // 2-element list
  case head :: tail    => // non-empty list
  case Nil             => // empty list
}

seq match {
  case Seq()             => // empty Seq
  case Seq(a, b)         => // 2-element Seq
  case Seq(a, tail @ _*) => destructures into a and iterable tail
}