将对象列表减少到密钥映射及其聚合计数的惯用法?

Era*_*dan 13 scala

我正在尝试在一个简单的Scala程序中重新创建Hadoop的字数映射/减少逻辑以供学习

这就是我到目前为止所拥有的

val words1 = "Hello World Bye World"      
val words2 = "Hello Hadoop Goodbye Hadoop"

val input = List(words1,words2)           
val mapped = input.flatMap(line=>line.split(" ").map(word=>word->1))
    //> mapped  : List[(String, Int)] = List((Hello,1), (World,1), (Bye,1), 
    //                                       (World,1), (Hello,1), (Hadoop,1), 
    //                                       (Goodbye,1), (Hadoop,1))

mapped.foldLeft(Map[String,Int]())((sofar,item)=>{
    if(sofar.contains(item._1)){
        sofar.updated(item._1, item._2 + sofar(item._1))
    }else{
        sofar + item
    }
})                              
    //>Map(Goodbye -> 1, Hello -> 2, Bye -> 1, Hadoop -> 2, World -> 2)
Run Code Online (Sandbox Code Playgroud)

这似乎有效,但我确信有一种更惯用的方法来处理reduce部分(foldLeft)

我在考虑也许是一个多图,但我有一种感觉,Scala有办法轻松地做到这一点

在那儿?例如,添加到地图的方法,如果密钥存在,而不是替换它,将值添加到现有值.我确定我已经在某个地方看到了这个问题,但找不到它,也没有找到答案.

我知道groupBy可能是在现实世界中这样做的方式,但我正在尝试尽可能接近上面链接中的原始地图/减少逻辑.

Dom*_*mra 13

您可以使用Scalaz的 |+|运营商,因为Maps是部分半群的类型类:

|+|操作是含半幺群mappend功能(一个Monoid是可以"加"在一起的任何"东西"很多东西都可以加在一起是这样的:字符串,整型,地图,列表选项等.举个例子:

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> val map1 = Map(1 -> 3 , 2 -> 4)
map1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 2 -> 4)

scala> val map2 = Map(1 -> 1, 3 -> 6)
map2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1, 3 -> 6)

scala> map1 |+| map2
res2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 4, 3 -> 6, 2 -> 4)
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下,而不是创建一个List[(String,Int)],创建一个List[Map[String,Int]],然后总结它们:

val mapped = input.flatMap(_.split(" ").map(word => Map(word -> 1)))
mapped.suml
Run Code Online (Sandbox Code Playgroud)

  • 我猜想.Scalaz基本上是一个"pimps"的库(使用Pimp my Library模式)Scala的默认库,有一些选择组件可以从Haskell和类别理论中获取思路.它的设计目的是让Scala以更加"经​​典功能"的方式使用,然后默认库允许.因此,您可以获得State monads,applicative functors,IO抽象等等.这需要一段时间才能理解,但真正有用. (3认同)
  • Scalaz +100.你越早学会它,你的生活就越好! (3认同)
  • 而不是mapped.reduce(_ | + | _),您只需调用mapped.suml(无参数)并获得相同的结果. (2认同)

Jan*_*Jan 8

您可以使用返回0的映射作为默认值.地图提供withDefaultValue:

def withDefaultValue[B1 >: B](d: B1): Map[A, B1]
Run Code Online (Sandbox Code Playgroud)

具有给定默认值的相同地图:

val emptyMap = Map[String,Int]().withDefaultValue(0)
mapped.foldLeft(emptyMap)((sofar,item) => {
    sofar.updated(item._1, item._2 + sofar(item._1))
})  
Run Code Online (Sandbox Code Playgroud)

  • 除此之外:`withDefaultValue`的替代方法是`sofar(item._1)`使用`sofar.get(item._1).getOrElse(0)` (2认同)

Era*_*kon 6

如果我错了,请纠正我但是这个怎么样:

val w = words.groupBy(_.toString).map(x => (x._1,x._2.size)).toList
Run Code Online (Sandbox Code Playgroud)

假设单词是单词列表:

val words1 = "Hello World Bye World"
val words2 = "Hello Hadoop Goodbye Hadoop"
val words = words1.split(" ") ++ words2.split(" ")
val w = words.groupBy(_.toString).map(x => (x._1,x._2.size)).toList
//List((Goodbye,1), (Hello,2), (Bye,1), (Hadoop,2), (World,2))
Run Code Online (Sandbox Code Playgroud)