【问题标题】:Flattening a map of sets展平集合图
【发布时间】:2014-09-17 12:53:36
【问题描述】:

我正在尝试将键可遍历的地图展平,从某种意义上说:

Map( Set(1, 2, 3) -> 'A', Set(4, 5, 6) -> 'B')

应该变平为:

Map(5 -> B, 1 -> A, 6 -> B, 2 -> A, 3 -> A, 4 -> B)

这是我所做的:

def fuse[A, B, T <: Traversable[A]](mapOfTravs: Map[T, B]): Map[A, B] = {
  val pairs = for {
    trav <- mapOfTravs.keys
    key <- trav
  } yield (key, mapOfTravs(trav))
  pairs.toMap
}   

它有效。但是:

  1. 有更简单的方法吗?

  2. 我对 Scala 类型系统不太满意,我相信这可以改进。每当我使用我的函数时,我都必须明确指定类型:

    val map2 = Map( Set(1, 2, 3) -> 'A', Set(4, 5, 6) -> 'B')
    val fused2 = fuse[Int, Char, Set[Int]](map2)
    
    val map1: Map[Traversable[Int], Char] = Map( Set(1, 2, 3) -> 'A', Set(4, 5, 6) -> 'B')
    val fused1 = fuse[Int, Char, Traversable[Int]](map1)
    

P.S.:这个fuse函数在key traversables有一个非空交集时没有多大意义。

【问题讨论】:

    标签: scala


    【解决方案1】:

    这基本上是你在 for 理解中所做的,但稍微简化了一点:

      def fuse[A, B, T <: Traversable[A]](mapOfTravs: Map[T, B]): Map[A, B] = {
        mapOfTravs.flatMap({ case (s, c) => s.map(i => i -> c) })
      }
    

    你对类型无能为力,我确信你可以做一些类型 lambda 恶作剧,我只是不知道该怎么做......

    更新 这里有一个稍微好一点的版本,和上面的 flatMap 一样:

      def fuse2[A, B, T <: Traversable[A]](mapOfTravs: Map[T, B]): Map[A, B] = {
        for {
          (keys, value) <- mapOfTravs
          key <- keys
        } yield key -> value
      }
    

    【讨论】:

    • 我实际上有这个表格,但我将其更改为for-comprehension,然后再将其发布在这里,因为它更具可读性。每当我看到flatMap(map()) 时,我都会将其转换为for-comprehension。
    • @toto2 使用更好版本的 for comprehension 进行更新,当它真的不需要时,您正在回调地图等
    【解决方案2】:

    就像 @Azzie,我在考虑 zip,但也许 Azzie 在这些 zees 方面具有优势。

    scala> val m = Map( Set(1, 2, 3) -> 'A', Set(4, 5, 6) -> 'B')
    m: scala.collection.immutable.Map[scala.collection.immutable.Set[Int],Char] = Map(Set(1, 2, 3) -> A, Set(4, 5, 6) -> B)
    
    scala> (m map { case (k, v) => k zip (Stream continually v) }).flatten.toMap
    res0: scala.collection.immutable.Map[Int,Char] = Map(5 -> B, 1 -> A, 6 -> B, 2 -> A, 3 -> A, 4 -> B)
    
    scala> (m map { case (k, v) => k zipAll (Nil, null, v) }).flatten.toMap
    res1: scala.collection.immutable.Map[Any,Char] = Map(5 -> B, 1 -> A, 6 -> B, 2 -> A, 3 -> A, 4 -> B)
    
    scala> m flatMap { case (k, v) => k zip (Stream continually v) }
    res2: scala.collection.immutable.Map[Int,Char] = Map(5 -> B, 1 -> A, 6 -> B, 2 -> A, 3 -> A, 4 -> B)
    

    如何很好地概括它并不明显。

    【讨论】:

    • 我总是忘记流。他们有什么需要担心的开销吗?
    【解决方案3】:

    这看起来很可怕,使用 0 有点作弊,但它确实有效

     m.map( {case (s,c) => s.zipAll(Set(c),0,c)} ).flatten.toMap
    

    【讨论】:

      【解决方案4】:

      因为我显然是在“可怕的通用隐含”踢lately

      import scala.collection.MapLike
      import scala.collection.TraversableLike
      import scala.collection.generic.CanBuildFrom
      
      implicit class Map_[
        A,
        B,
        T1 : ({type L[X] = X => TraversableLike[A, T2]})#L,
        T2,
        M1 : ({type L[X] = X => MapLike[T1, B, M2]})#L, 
        M2 <: MapLike[T1, B, M2] with Map[T1, B]
      ](map: M1) {
      
        def fuse[M3](implicit cbfM: CanBuildFrom[M2, (A, B), M3]) : M3 =
          map.flatMap({ case (k, v) => k.toTraversable.map((_, v)) })
      }
      

      例子:

      scala> Map(Set(1, 2, 3) -> 'A', Set(4, 5, 6) -> 'B').fuse
      res: scala.collection.immutable.Map[Int,Char] =
           Map(5 -> B, 1 -> A, 6 -> B, 2 -> A, 3 -> A, 4 -> B)
      
      scala> Map(Array(1, 2, 3) -> 'A', Array(4, 5, 6) -> 'B').fuse
      res: scala.collection.immutable.Map[Int,Char] =
           Map(5 -> B, 1 -> A, 6 -> B, 2 -> A, 3 -> A, 4 -> B)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-15
        • 1970-01-01
        • 2011-07-23
        • 2020-05-07
        • 2017-10-08
        相关资源
        最近更新 更多