代码之家  ›  专栏  ›  技术社区  ›  Max Heiber

如何在Scala中执行嵌套groupBy

  •  4
  • Max Heiber  · 技术社区  · 10 年前

    我有一个案例对象列表,如下所示:

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

    编辑

    类似于 Specter Clojure图书馆可能在这里有所帮助。

    3 回复  |  直到 10 年前
        1
  •  2
  •   Dani    10 年前

    怎么样:

    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 调用,但这是我能够组织代码的最方便的方式。

        2
  •  0
  •   Max Heiber    10 年前

    给定一个列表 AppInfo case对象已调用 appInfos 做 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)))
    

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

        3
  •  0
  •   Max Heiber    10 年前

    案例类将为您提供比地图更好的类型安全性,但更丑陋:

    给定一个列表 AppInfo case对象已调用 appInfos 做 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