如何在 Scala 中做一个嵌套的 groupBy
How to do a nested groupBy in Scala
我有一个这样的案例对象列表:
AppInfo(client = "client1", project = "project1", version = "version1")
AppInfo(client = "client1", project = "project1", version = "version2")
AppInfo(client = "client2", project = "project3", version = "version1")
AppInfo(client = "client2", project = "project4", version = "version1")
并且需要制作这样的嵌套结构:
Map(
"clients" -> List(
Map(
"name" -> "client1",
"projects" -> List(
Map(
"name" -> "project1",
"versions" -> List(
"version1",
"version2"
)
)
)
),
Map(
"name" -> "client2",
"projects" -> List(
Map(
"name" -> "project3",
"versions" -> List(
"version1"
)
),
Map(
"name" -> "project4",
"versions" -> List(
"version1"
)
)
)
)
)
)
这看起来很糟糕,但它会序列化为这个非常简单的 JSON:
{
"clients": [
{
"name": "client1",
"projects": [
{
"name": "project1",
"versions": [
"version1",
"version2"
]
}
]
},
{
"name": "client2",
"projects": [
{
"name": "project3",
"versions": [
"version1"
]
},
{
"name": "project4",
"versions": [
"version1"
]
}
]
}
]
}
有什么合理的方法吗?现在我在列表中的 groupBys 中的地图中有地图。
编辑
Clojure 的 Specter 库之类的东西可能在这里有所帮助。
给出名为 appInfos
的 AppInfo
个案例对象列表,执行 byClient(appInfos)
:
def byVersion(appInfos: List[AppInfo]) = appInfos
.map(_.version)
def byProject(appInfos: List[AppInfo]) = appInfos
.groupBy(_.project)
.map(tuple => Map("name" -> tuple._1, "versions" -> byVersion(tuple._2)))
def byClient(appInfos: List[AppInfo]) = appInfos
.groupBy(_.client)
.map(tuple => Map("name" -> tuple._1, "projects" -> byProject(tuple._2)))
它是重复的,但可以完成工作。
Case 类 会比 map 提供更好的类型安全性,但更丑陋:
给定一个名为 appInfos
的 AppInfo
个案例对象列表,执行 byClient2(appInfos)
:
case class ProjectApps(name: String, versions: List[String])
object ProjectApps{
def apply(tuple: (String, List[AppInfo])): ProjectApps =
ProjectApps(name=tuple._1, versions=tuple._2.map(_.version))
}
case class ClientApps(name: String, projects: List[ProjectApps])
object ClientApps {
def apply(tuple: (String, List[AppInfo])): ClientApps = {
val name = tuple._1
val projects = tuple._2.groupBy(_.project).map(ProjectApps.apply).toList
ClientApps(name, projects)
}
}
def byClient2(appInfos: List[AppInfo]): List[ClientApps] =
appInfos.groupBy(_.client).map(ClientApps.apply).toList
怎么样:
for {
(clientName, clientInfos) <- infoList.groupBy(_.client)
} yield {
val clientProjects = clientInfos.groupBy(_.project)
val projectSection = clientProjects.map { case(name, infos) => Map("name" -> name, "versions" -> infos.map(_.version)) }
Map("name" -> clientName, "projects" -> projectSection)
}
它不会减少 map
或 groupBy
调用的次数,但这是我能够组织代码的最方便的方式。
我有一个这样的案例对象列表:
AppInfo(client = "client1", project = "project1", version = "version1")
AppInfo(client = "client1", project = "project1", version = "version2")
AppInfo(client = "client2", project = "project3", version = "version1")
AppInfo(client = "client2", project = "project4", version = "version1")
并且需要制作这样的嵌套结构:
Map(
"clients" -> List(
Map(
"name" -> "client1",
"projects" -> List(
Map(
"name" -> "project1",
"versions" -> List(
"version1",
"version2"
)
)
)
),
Map(
"name" -> "client2",
"projects" -> List(
Map(
"name" -> "project3",
"versions" -> List(
"version1"
)
),
Map(
"name" -> "project4",
"versions" -> List(
"version1"
)
)
)
)
)
)
这看起来很糟糕,但它会序列化为这个非常简单的 JSON:
{
"clients": [
{
"name": "client1",
"projects": [
{
"name": "project1",
"versions": [
"version1",
"version2"
]
}
]
},
{
"name": "client2",
"projects": [
{
"name": "project3",
"versions": [
"version1"
]
},
{
"name": "project4",
"versions": [
"version1"
]
}
]
}
]
}
有什么合理的方法吗?现在我在列表中的 groupBys 中的地图中有地图。
编辑
Clojure 的 Specter 库之类的东西可能在这里有所帮助。
给出名为 appInfos
的 AppInfo
个案例对象列表,执行 byClient(appInfos)
:
def byVersion(appInfos: List[AppInfo]) = appInfos
.map(_.version)
def byProject(appInfos: List[AppInfo]) = appInfos
.groupBy(_.project)
.map(tuple => Map("name" -> tuple._1, "versions" -> byVersion(tuple._2)))
def byClient(appInfos: List[AppInfo]) = appInfos
.groupBy(_.client)
.map(tuple => Map("name" -> tuple._1, "projects" -> byProject(tuple._2)))
它是重复的,但可以完成工作。
Case 类 会比 map 提供更好的类型安全性,但更丑陋:
给定一个名为 appInfos
的 AppInfo
个案例对象列表,执行 byClient2(appInfos)
:
case class ProjectApps(name: String, versions: List[String])
object ProjectApps{
def apply(tuple: (String, List[AppInfo])): ProjectApps =
ProjectApps(name=tuple._1, versions=tuple._2.map(_.version))
}
case class ClientApps(name: String, projects: List[ProjectApps])
object ClientApps {
def apply(tuple: (String, List[AppInfo])): ClientApps = {
val name = tuple._1
val projects = tuple._2.groupBy(_.project).map(ProjectApps.apply).toList
ClientApps(name, projects)
}
}
def byClient2(appInfos: List[AppInfo]): List[ClientApps] =
appInfos.groupBy(_.client).map(ClientApps.apply).toList
怎么样:
for {
(clientName, clientInfos) <- infoList.groupBy(_.client)
} yield {
val clientProjects = clientInfos.groupBy(_.project)
val projectSection = clientProjects.map { case(name, infos) => Map("name" -> name, "versions" -> infos.map(_.version)) }
Map("name" -> clientName, "projects" -> projectSection)
}
它不会减少 map
或 groupBy
调用的次数,但这是我能够组织代码的最方便的方式。