【问题标题】:How to do a nested groupBy in Scala如何在 Scala 中进行嵌套 groupBy
【发布时间】:2016-05-13 04:04:33
【问题描述】:

我有一个这样的案例对象列表:

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 中的 Maps 中有地图。

编辑

类似于 Clojure 的 Specter 库之类的东西在这里可能会有所帮助。

【问题讨论】:

    标签: json scala collections group-by nested


    【解决方案1】:

    怎么样:

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

    它不会减少 mapgroupBy 调用的数量,但这是我能够组织代码的最方便的方式。

    【讨论】:

    • 谢谢,我试试看!我喜欢你如何使用模式匹配来避免像 tuple._1tuple._2 这样的事情。
    【解决方案2】:

    案例类会给你比地图更好的类型安全,但更丑陋:

    给定一个名为appInfosAppInfo 案例对象列表做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
    

    【讨论】:

    • 标记为答案,因为类型安全性很好,并且使用案例类可以更轻松地生成 JSON 模式或 Swagger 文档等内容。不过它是丑陋的
    【解决方案3】:

    给定一个名为appInfosAppInfo 案例对象列表,执行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)))
    

    这是重复的,但可以完成工作。

    【讨论】:

      猜你喜欢
      • 2015-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-30
      • 1970-01-01
      • 1970-01-01
      • 2017-01-20
      • 2015-04-21
      相关资源
      最近更新 更多