简体   繁体   English

如何基于scala中深度嵌套的列表字段展开对象?

[英]How to unwind an object based on deeply nested list fields in scala?

Simplified Scenario简化场景

Lets take a simplified example with 2 levels of nested fields in a case class.让我们举一个简单的例子,在一个案例类中包含2 levels的嵌套字段。

Suppose I have a case class Person as shown below:假设我有一个case class Person ,如下所示:

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

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

Suppose I have an object for the above Person class as shown below:假设我有一个用于上述 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")
      )
    )
  )

Now, I want to unwind this object person1 in such a way that I get following flattened list of tuples:现在,我想以如下方式unwind这个对象person1

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

Now, in this simple scenario, what I could do to unwind is as follow:现在,在这个简单的场景中,我可以做的放松如下:

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

Question

In my real scenario, I can have n levels of nesting in the case class.在我的真实场景中,我可以在 case 类中有n levels嵌套。 So, it's not feasible for me to write n lines of flatMaps for each inner nested field to produce such tuple.因此,为每个内部嵌套字段编写 n 行 flatMap 来生成这样的元组对我来说是不可行的。 Is there a generic( if possible ), better, shorter yet functional way to achieve this in scala ?是否有通用(如果可能)、更好、更短但功能强大的方法来在 Scala 中实现这一点?

You can write your example code using for :您可以使用for编写示例代码:

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

With this version it is easy to add as many levels of nesting as are required.使用此版本可以轻松添加所需的嵌套级别。

Another option is to give each nested class a flat method that creates the flattened version of the class using the flat method of the contained classes.另一种选择是给每个嵌套类一个flat创建使用的类的扁平版本方法flat包含的类的方法。 This means that calling code does not need to know the nested structure, it just calls flat on the outer object.这意味着调用代码不需要知道嵌套结构,它只是在外部对象上调用flat Or a typeclass could used if you can't change the original classes.或者,如果您无法更改原始类,则可以使用类型类。

This question seems contradictory – on the one hand, you're saying that you can have n levels of nesting (where n is presumably not known at compile time), and you therefore can't write n levels of flatMap s.这个问题似乎是矛盾的——一方面,你说你可以有n级嵌套(其中n在编译时可能不知道),因此你不能写nflatMap s。 But on the other hand you're saying you want to return a tuple – but the length of the tuple in your example corresponds to the depth of nesting, meaning that n is the same as the length of the tuple and thus a compile-time constant.但另一方面,你说你想返回一个元组——但你的例子中元组的长度对应于嵌套的深度,这意味着n与元组的长度相同,因此编译时持续的。

Anyway, if you need arbitrary degrees of nesting, you need to use recursion.无论如何,如果您需要任意程度的嵌套,则需要使用递归。

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")))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM