[英]How to unwind an object based on deeply nested list fields in scala?
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)
}
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
在编译时可能不知道),因此你不能写n
级flatMap
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.