【问题标题】:Scala How to Intersect Maps of types Map[String, Double] inside a List of: List[Map[String, Double]]Scala如何在以下列表中相交类型Map [String,Double]的映射:List [Map [String,Double]]
【发布时间】:2021-02-15 11:58:06
【问题描述】:

所以我有一个地图列表:List[Map[String, Double]]。 一个例子是:

List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9), 
        Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8), 
        Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4), 
        Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))

我想要做的是将所有地图的交集放在一起,所以它看起来像:

   For example, for A: (1.1 + 0.8 + 3.4 + 3.7)/4 = 2.25
                for D: (0.9 + 2.8 + 2.9 + 1.8)/4 = 2.1

   List(Map("A" -> 2.25,"D" -> 2.1))

有没有办法只使用内置函数来获取上面地图的相交列表?这些值是四个地图组合中所有键的平均值。

【问题讨论】:

  • 相交到底是什么意思?你从哪里得到2.252.1?如果你有一个函数f 已经与两个地图相交,你可以做list.reduce(f)list.foldLeft(Map())(f)
  • 我知道您只想获取重复的键,但是值的逻辑是什么?均值? - 另外,你对外部图书馆开放吗? x a 没有开箱即用的 std 函数,但 cata 确实提供了更接近的东西。
  • 尽可能不使用外部库。是否可以使用 intersect() 函数和添加的其他函数来做到这一点?

标签: list scala maps intersect


【解决方案1】:

尝试先使用reduce 只保留重复键并将所有值相加,然后使用mapValues 求平均值:

val maps = List(...)

val intersected = maps
  .reduce { (m1, m2) =>
    m1.keySet.intersect(m2.keySet).map(key => (key, m1(key) + m2(key))).toMap
  }
  .view
  .mapValues(_ / maps.size)
  .toMap

Scastie

This question 类似。

【讨论】:

    【解决方案2】:

    必须注意输入不为空。

    val lm : List[Map[String,Double]] =
      List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9)
          ,Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8)
          ,Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4)
          ,Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))
    
    val len = lm.length
    val res = if (len > 0)
                lm.map(_.keySet)
                  .reduce(_ intersect _)
                  .map(k => (k, lm.map(_(k)).sum/len))
                  .toMap
              else Map.empty[String,Double]
    //res: Map[String,Double] = Map(A -> 2.25, D -> 2.1)
    

    【讨论】:

    • 哦,忘记输入为空的情况了。 +1
    【解决方案3】:

    如果您愿意使用外部库,使用 cats 会变得非常简单:

    import cats.data.NonEmptyList
    import cats.syntax.all._
    
    val data = NonEmptyList.of(
      Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9),
      Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8),
      Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4),
      Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7)
    )
    
    val result =
      data
        .nonEmptySequence
        .fmap { group =>
          val (sum, count) = group.foldMap(_ -> 1)
          sum / count
        }
    // result: Map[String, Double] = HashMap(A -> 2.25, D -> 2.1)
    

    可以看到运行here的代码

    【讨论】:

      【解决方案4】:

      假设我们有:

      val list = List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9),
        Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8),
        Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4),
        Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))
      

      您的另一个选择是(仅 Scala 2.13):

      val stringToDoubles =
        list.flatten
          .groupMap(_._1)(_._2)
          .filter(_._2.size == list.size)
          .map(keyAndValues => (keyAndValues._1, keyAndValues._2.sum / list.size))
      

      代码运行可以在scastie找到。

      在 Scala 2.12 及以下版本中:

      val stringToDoubles =
        list.flatten
          .groupBy(_._1)
          .filter(_._2.size == list.size)
          .map(keyAndValues => (keyAndValues._1, keyAndValues._2.map(_._2).sum / list.size))
      

      代码运行可以在scastie找到。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-01-27
        • 2014-08-15
        • 2021-04-21
        • 2011-08-31
        • 1970-01-01
        • 2023-03-23
        • 2019-11-27
        • 1970-01-01
        相关资源
        最近更新 更多