【问题标题】:Processing Set of Sets and return a flat Iterable处理 Set 集合并返回一个平面 Iterable
【发布时间】:2011-06-12 09:48:39
【问题描述】:
val input=Set(Set("a","b"),Set("b","c"))

我想要这个:

Map("a"->1,"b"->2,"c"->1)

实现此类功能的最佳功能方法是什么? 使用 yield 关键字会导致嵌套的 Iterables:

output = for(firstlevel<-input) yield for(item<-firstlevel) yield item

【问题讨论】:

    标签: scala functional-programming yield loops


    【解决方案1】:

    更新: 加入了使用input.toSeq.flatten的建议
    而不是input.toSeq flatMap { _.toSeq }

    转换为单个值序列...

    input.toSeq.flatten
    

    ...对匹配的值进行分组...

    input.toSeq.flatten groupBy { identity }
    

    ...和计数

    input.toSeq.flatten groupBy { identity } mapValues { _.size }
    

    【讨论】:

    • 为什么不使用这种语法? input.toSeq.flatMap( .toSeq ).groupBy(identity).mapValues(.size)
    • 其中一些是个人喜好 - 例如放置大括号和制表符与空格的位置 - 但我发现使用中缀表示法编写的代码通常更干净,更易于阅读。
    • 附注我知道你打算写input.toSeq.flatMap(_.toSeq ).groupBy(identity).mapValues(_.size)。那是你的wiki格式。我发现在 SO 上添加评论时将代码包装在反引号中会有所帮助
    • 还可以更简单:output ={input.toSeq flatten } groupBy { identity } mapValues { _.size }
    【解决方案2】:

    如果你想使用 for-comprehension 和 yield:

    output = for{
        (set,idx) <- input.zipWithIndex
        item <- set
    } yield (item -> idx)

    最后一行中的代码可以简化(但不是您想要的):

    output = for{
        set <- input
        item <- set
    } yield item

    【讨论】:

      【解决方案3】:

      天哪,太丑了……

      input.foldLeft(Map[String,Int]())((m,s) => 
         s.foldLeft(m)((n,t) => n + (t -> (1 + n.getOrElse(t,0)))))
      

      [编辑]

      Collection-API 确实需要一种“合并”两个地图的方法(或者我只是忽略了它???),例如

      def merge[A,B](m1: Map[A,B], m2:Map[A,B])(f: (B,B)=>B):Map[A,B] =
        m1.foldLeft(m2)((m,t) =>
            m + (t._1 -> m.get(t._1).map(k => f(k,t._2)).getOrElse(t._2)))
      

      有了这个,你可以写这样的东西:

      input.map(_.map(x => x -> 1).toMap).reduceLeft(merge(_,_)(_+_))
      

      [编辑2]

      与凯文的想法合并可以写成

      def merge[A,B](m1: Map[A,B], m2:Map[A,B])(f: (B,B)=>B):Map[A,B] =
         m1.keys ++ m2.keys map {k => k ->
              List(m1.get(k), m2.get(k)).flatten.reduceLeft(f)} toMap
      

      看来我的 Scala-Fu 还是太弱了。最好的表达方式是什么

      (o1,o2) match {
          case (Some(x),Some(y)) => Some(f(x,y))    
          case (Some(x), _) => Some(x)    
          case (_, Some(y)) => Some(y)    
          case => error("crack in the time-space-continuum")  
      }
      

      ?

      【讨论】:

      • 这让我学习了折叠和柯里化函数,我认为 fold 在很多情况下都很好,但在这种情况下我更喜欢使用 Kevin 的解决方案,因为它比 fold 方法更具声明性和可读性
      • Scala 已经可以进行合并,但您必须明确使用collection.immutable.Map 而不是collection.Map。然后你会写val merged = m1 withDefault m2,这取决于Map[K,V] 扩展A=&gt;B
      • 实际上,这仍然不会让您遍历合并的键。你需要更像{val m1m2 = m1 withDefault m2; m1.keys++m2.keys map {k =&gt; k-&gt;m1m2(k)} toMap}
      • @Kevin:当两个地图中都有一个键时,我需要一种将值相加的方法,所以我看不出 withDefault 在这里有什么帮助。
      • @Landei:啊,我明白了,可能是某种基于索引的 zip,采用 Map[K,V1]Map[K,V2] 并返回 Map[K,(Option[V1],Option[V2])]
      猜你喜欢
      • 2019-10-08
      • 2020-11-16
      • 1970-01-01
      • 2015-11-12
      • 1970-01-01
      • 2021-05-07
      • 1970-01-01
      • 2017-08-25
      • 2019-12-31
      相关资源
      最近更新 更多