【问题标题】:How can I group by the individual elements of a list of elements in Scala如何按 Scala 中元素列表的单个元素进行分组
【发布时间】:2021-11-26 01:27:29
【问题描述】:

如果我没有用它们的实际名称来命名,请原谅我,我刚刚开始学习 Scala。我已经环顾了一段时间,但找不到我的问题的明确答案。

假设我有一个对象列表,每个对象都有两个字段:x: Intl: List[String],在我的例子中,字符串代表类别。

l 列表可以是任意长度,因此一个对象可以属于多个类别。此外,各种对象可以属于同一类别。我的目标是按各个类别对对象进行分组,其中类别是键。这意味着如果一个对象被链接到“N”个类别,它将出现在“N”个键值对中。

到目前为止,我设法通过以下方式对类别列表进行分组: objectList.groupBy(x => x.l) 但是,这显然是按类别列表而不是按类别对对象进行分组。

我正在尝试使用不可变集合来避免循环等。

如果有人有一些想法,将不胜感激! 谢谢

编辑: 通过请求实际案例类和我正在尝试的内容。 case class Car(make: String, model: String, fuelCapacity: Option[Int], category:Option[List[String]]) 再一次,汽车可以属于多个类别。比如说List("SUV", "offroad", "family")

我想按category 元素而不是整个类别列表进行分组,并将fuelCapacity 作为值,以便能够提取每个类别的平均fuelCapacity 以及其他指标。

【问题讨论】:

    标签: scala


    【解决方案1】:

    类似以下内容?

    objectList // Seq[YourType]
      .flatMap(o => o.l.map(c => c -> o)) // Seq[(String, YourType)]
      .groupBy { case (c,_) => c } // Map[String,Seq[(String,YourType)]]
      .mapValues { items => c -> items.map { case (_, o) => o } } // Map[String, Seq[YourType]]
    

    (特意“重”以帮助大家理解背后的思想)

    编辑,或从 Scala 2.13 开始感谢groupMap

    objectList // Seq[YourType]
      .flatMap(o => o.l.map(c => c -> o)) // Seq[(String, YourType)]
      .groupMap { case (c,_) => c } { case (_, o) => o } // Map[String,Seq[YourType]]
    

    【讨论】:

    • 所以 flatMap 返回一个元组序列Seq[(String, YourType)]。我应该以每个元组包含一个类别和一个对象的方式来解释这一点吗?那么说一个对象属于 N 个类别,该对象映射到 N 个单独的元组?然后 groupBy 将所有在tuple._1处具有相同类别字符串的元组分组?
    • 因为您提供的 flatMap 代码似乎没有给出这个结果。我从中得到的结果是:List[(List[String], YourType)]。我对 flatmap 的理解是,这会使所有内部列表变平?为什么类别仍然在元组内的列表中?
    • 您可能应该添加您在原始帖子中使用的实际案例类,或者使用您尝试过的代码和您遇到的问题打开一个新案例类。没有看到就很难提供帮助。
    • 你是对的,只是添加了那个。试图保持一般性,但这可能更清楚。
    • 好的,所以与我期望的不同之处在于你得到了一个Option[List]。将第一个 flatMao 替换为 o.l.getOrElse(Seq()) 而不是 o.l
    【解决方案2】:

    以您的 EDIT 为指导。

    case class Car( make: String
                  , model: String
                  , fuelCapacity: Option[Int]
                  , category:Option[List[String]] )
    
    val cars: List[Car] = ???
    
    //all currently known category strings
    val cats: Set[String] = cars.flatMap(_.category).flatten.toSet
    
    //category -> list of cars in this category
    val catMap: Map[String,List[Car]] =
      cats.map(cat => (cat, cars.filter(_.category.contains(cat)))).toMap
    
    //category -> average fuel capacity for cars in this category 
    val fcAvg: Map[String,Double] =
      catMap.map{case (cat, cars) =>
        val fcaps: List[Int] = cars.flatMap(_.fuelCapacity)
        if (fcaps.lengthIs < 1) (cat, -1d)
        else (cat, fcaps.sum.toDouble / fcaps.length)
      }
    
    

    【讨论】:

      【解决方案3】:

      您非常接近,您只需要在group 之前拆分列表中的每个单独元素,因此请尝试以下操作:

      // I used a Set instead of a List,
      // since I don't think the order of categories matters
      // as well I would think having two times the same category doesn't make sense.
      final case class MyObject(x: Int, categories: Set[String] = Set.empty) {
        def addCategory(category: String): MyObject =
          this.copy(categories = this.categories + category)
      }
      
      def groupByCategories(data: List[MyObject]): Map[String, List[Int]] =
        data
          .flatMap(o => o.categories.map(c => c -> o.x))
          .groupMap(_._1)(_._2)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-09
        • 2011-08-13
        • 2012-05-01
        • 2011-06-27
        相关资源
        最近更新 更多