13

学習用の単純な Scala プログラムでHadoop のワード カウントマップ/reduce ロジックを再作成しようとしています

これは私がこれまでに持っているものです

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)

これは機能しているようですが、reduce 部分 (foldLeft) を処理するためのより慣用的な方法があると確信しています。

おそらくマルチマップについて考えていましたが、Scala にはこれを簡単に行う方法があると感じています。

ある?たとえば、マップに追加する方法、およびキーが存在する場合は、それを置き換える代わりに、値を既存の値に追加します。この質問をどこかで見たことがあると思いますが、見つけることができず、答えも見つかりませんでした。

おそらく現実の世界でそれを行う方法であることは知っgroupByていますが、上記のリンクの元のマップ/リデュースロジックにできるだけ近い方法で実装しようとしています。

4

5 に答える 5

13

Semigroup 型クラスの一部であるため、Scalaz |+|演算子を使用できます。Maps

|+|演算子はモノイド関数です (モノイドとは、mappend一緒に「追加」できる任意の「もの」です。多くのものは、このように一緒に追加できます: 文字列、整数、マップ、リスト、オプションなど。例:

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)

したがって、あなたの場合List[(String,Int)]は、 を作成し、 を作成しList[Map[String,Int]]、それらを合計するのではなく:

val mapped = input.flatMap(_.split(" ").map(word => Map(word -> 1)))
mapped.suml
于 2012-12-14T01:17:02.373 に答える
8

デフォルト値として 0 を返すマップを使用できます。Map オファーの DefaultValue:

def withDefaultValue[B1 >: B](d: B1): Map[A, B1]

指定されたデフォルト値を持つ同じマップ:

val emptyMap = Map[String,Int]().withDefaultValue(0)
mapped.foldLeft(emptyMap)((sofar,item) => {
    sofar.updated(item._1, item._2 + sofar(item._1))
})  
于 2012-12-13T21:13:23.500 に答える
4

別のバージョン:

 val words1 = "Hello World Bye World"             
//> words1  : java.lang.String = Hello World Bye World
 val words2 = "Hello Hadoop Goodbye Hadoop"       
//> words2  : java.lang.String = Hello Hadoop Goodbye Hadoop

 val words = words1.split(" ") ++ words2.split(" ")
//> words  : Array[java.lang.String] = Array(Hello, World, Bye, World, Hello, Hadoop, Goodbye, Hadoop)

 words.map(m => (m, (0 /: words)
   ((x, y) => if (y == m) x + 1 else x))).
     toList.distinct.toMap
 //> res0: scala.collection.immutable.Map[java.lang.String,Int] = Map(Goodbye -> 1, Hello -> 2, Bye -> 1, Hadoop -> 2, World -> 2)
于 2012-12-14T11:19:40.777 に答える
2

からScala 2.13、ほとんどのコレクションはgroupMapReduceメソッドで提供されます。これは、Hadoop's map/reduceロジックの類似物と見なすことができます。

val words = List(words1, words2).flatMap(_.split(" "))

words.groupMapReduce(identity)(_ => 1)(_ + _)
// immutable.Map[String,Int] = HashMap(Goodbye -> 1, Hello -> 2, Bye -> 1, Hadoop -> 2, World -> 2)

これ:

  • 2 つの入力リストから単語を分割してマージします

  • groups 要素自体 (同一性) (グループMapReduce のグループ部分)

  • mapグループ化された各値の出現を 1 にする (グループMap Reduce のマップ部分)

  • reduces 値のグループ内の値 ( _ + _) を合計する (groupMap Reduceの一部を減らす)。

これは、次の方法で翻訳できるワンパス バージョンです。

words.groupBy(identity).mapValues(_.map(_ => 1).reduce(_ + _))
于 2018-10-09T18:26:22.807 に答える