【问题标题】:How to unwind an object based on deeply nested list fields in scala?如何基于scala中深度嵌套的列表字段展开对象?
【发布时间】:2021-01-28 00:21:17
【问题描述】:

简化场景

让我们以 2 levels 的案例类中的嵌套字段为例。

假设我有一个case class 人,如下所示:

case class Address(
  name: String,
  zipCodes: List[String]
)

case class Person(
  name: String,
  address: List[Address]
)

假设上面的 Person 类有一个object,如下所示:

val person1: Person = Person(
    name = "John",
    address = List(
      Address(
        name = "Address A",
        zipCodes = List("123","345")
      ),
      Address(
        name = "Address B",
        zipCodes = List("456","678")
      )
    )
  )

现在,我想 unwind 这个对象 person1 以这样的方式得到以下元组的扁平列表:

List(    
    (John, Address A, 123)
    (John, Address A, 345)
    (John, Address B, 456)
    (John, Address B, 678)
)

现在,在这个简单的场景中,我可以做如下放松:

person1.address.flatMap{
    address => address.zipCodes.map{
      zipCode =>
        (person1.name, address.name, zipCode)
    }

问题

在我的真实场景中,我可以在案例类中嵌套n levels。所以,对于我来说,为每个内部嵌套字段编写 n 行 flatMaps 来生成这样的元组是不可行的。是否有通用(如果可能),更好,更短但功能强大的方式来在 scala 中实现这一目标?

【问题讨论】:

标签: java scala functional-programming


【解决方案1】:

您可以使用for 编写示例代码:

for {
  address <- person1.address
  zipCode <- address.zipCodes
} yield {
  (person1.name, address.name, zipCode)
}

使用此版本,可以轻松添加所需的嵌套级别。

另一种选择是为每个嵌套类提供一个flat 方法,该方法使用所包含类的flat 方法创建该类的扁平化版本。这意味着调用代码不需要知道嵌套结构,它只是在外部对象上调用flat。或者,如果您无法更改原始类,则可以使用类型类。

【讨论】:

    【解决方案2】:

    这个问题似乎是矛盾的——一方面,你说你可以有n 的嵌套级别(其中n 在编译时可能是未知的),因此你不能写@987654323 @flatMaps 的级别。但另一方面,你说你想返回一个元组——但你的例子中元组的长度对应于嵌套的深度,这意味着 n 与元组的长度相同,因此是编译-时间常数。

    无论如何,如果你需要任意程度的嵌套,你需要使用递归。

    case class RoseTree[A](
      a: A,
      children: List[RoseTree[A]]
    )
    
    def unnest[A](tree: RoseTree[A]): List[List[A]] =
      tree match {
        case RoseTree(a, Nil) =>
          List(List(a))
        case RoseTree(a, children) =>
          children.flatMap(x => unnest(x).map(a :: _))
      }
    
    val example = 
      RoseTree("John", List(
        RoseTree("Address A", List(
          RoseTree("123", Nil),
          RoseTree("345", Nil))),
        RoseTree("Address B", List(
          RoseTree("456", Nil),
          RoseTree("678", Nil))))
    
    assert(unnest(example) == List(
      List("John", "Address A", "123"),
      List("John", "Address A", "345"),
      List("John", "Address B", "456"),
      List("John", "Address B", "678")))
    
    

    【讨论】:

      猜你喜欢
      • 2012-02-05
      • 2018-10-29
      • 2012-09-20
      • 1970-01-01
      • 1970-01-01
      • 2016-05-18
      • 2021-10-14
      • 2020-09-23
      • 2017-06-29
      相关资源
      最近更新 更多