Scala map集合合并不覆盖重复key值,函数foldLeft

简单介绍一下,Scala语言中,foldLeft函数的三个使用场景:

  • 列表内数值的相加,作用相当于sum;
  • 变换后的Map集合有重复key值,并且需要根据key值合并
  • 合并Map重复key值统计词长度

场景一:列表数值相加,先上代码:

scala> val list1 = List(1,2,3,4,5)
list1: List[Int] = List(1, 2, 3, 4, 5)

scala> println(list1.foldLeft(0) { (sum, i) => sum + i})
15

scala> println(list1.sum)
15

从代码中可以看出,foldLeft函数此时的作用相当于sum。foldLeft后加圆括号,圆括号中写入初始值,因为要算整个List中每一个元素的和,因此,此时初始值为Int型的0。 之后的 (sum, i) => sum + i} 则表示,对于每一个List中的i元素,将i与sum相加,并将结果保存在sum中。foldLeft是从左往右遍历,而与它相对应的是函数foldRight,foldRight从右向左遍历。

场景二:合并两个Map,重复key值相加,不覆盖

在Scala的Map集合操作中,没有直接将两个Map根据key值合并value的操作,若是直接将两个Map相加,那么相同的key的值将会被后边的值覆盖,例如:

scala> val map1 = Map("aa" -> 1.0, "bb" -> 2.0)
map1: scala.collection.immutable.Map[String,Double] = Map(aa -> 1.0, bb -> 2.0)

scala> val map2 = Map("aa" -> 2.0, "cc" -> 4.0)
map2: scala.collection.immutable.Map[String,Double] = Map(aa -> 2.0, cc -> 4.0)

scala> println(map1 ++ map2)
Map(aa -> 2.0, bb -> 2.0, cc -> 4.0)

可见,直接暴力相加的话会覆盖重复key的value值,如果想计算不同长度单词的词频,单纯将map中的key变换为key的长度,再对value进行操作是不可取的。
可以使用foldLeft函数实现:

scala> val map1 = Map("a" -> 1, "b" -> 2)
map1: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)

scala> val map2 = Map("b" -> 4, "c" -> 8)
map2: scala.collection.immutable.Map[String,Int] = Map(b -> 4, c -> 8)

scala> val mergeMap = map1.foldLeft(map2) {
     | case (newMap, (name, score)) => 
     | newMap + (name -> (score + map2.getOrElse(name, 0)))
     | }
mergeMap: scala.collection.immutable.Map[String,Int] = Map(b -> 6, c -> 8, a -> 1)

需要说明的是,newMap为定义的新map,name 和 score为模式匹配的新定义名称,这三个变量虽然都是新定义变量,但是都不需要提前val/var定义,可直接使用。此时 newMap 的数据类型为 Map[String,Int],name的数据类型为String,score的数据类型为Int
根据结果可知,经过foldLeft之后,map相同key的value没有被覆盖,而是相加合并了。

场景三: 合并Map重复key值统计词长度

若一个Map中key为单词,value为这个单词的词频,我们若想根据单词的长度统计不同长度单词的总词频,就会需要foldLeft函数出场

foldLeft可以对map的value值进行相加,再这个场景下可以这样写:

scala> val wordsMap = Map("apple" -> 20, "pear" -> 10, "pineapple" -> 25, "grape" -> 30)
wordsMap: scala.collection.immutable.Map[String,Int] = Map(apple -> 20, pear -> 10, pineapple -> 25, grape -> 30)

scala> val wordFreqCount = wordsMap.foldLeft(Map.empty[Int, Int]) {
     |       case (newMap, (fruit, count)) =>
     |         newMap + (fruit.length -> (count + newMap.getOrElse(fruit.length, 0)))
     |     }
wordFreqCount: scala.collection.immutable.Map[Int,Int] = Map(5 -> 50, 4 -> 10, 9 -> 25)

首先在foldLeft函数中传入参数,即新map的初始值Map.empty[Int, Int], 然后根据case模式匹配,定义新map为newMap,对每一个原map中的值(fruit, count), 拿出fruit的单词length,并且相加,赋到newMap中,就能得到我们想要的结果。

你可能感兴趣的:(Scala map集合合并不覆盖重复key值,函数foldLeft)