2016-09-23 56 views
0

我正在寻找一种将scala数组转换为包含数组中发生项目频率的映射的整洁方式。将scala数组转换为项目数映射

例如, :

鉴于像数组:

val arr = Array("one", "one", "two", "three", "one", "three") 

我要地图:

Map("one" -> 3, "two" -> 1, "three" -> 2) 

我可以写一个函数做到这一点,如

import scala.collection.mutable 

def counter[T](arr: Array[T]) = { 
    val temp = mutable.Map[T, Int]() 
    for (i <- arr) { 
    if (temp.contains(i)) temp(i) += 1 
    else temp(i) = 1 
    } 
    temp 
} 

counter(arr) 

我想了解这是否可以更有效地完成。

回答

4

我会使用groupBy(identity)mapValues(_.length)

scala> val arr = Array("one", "one", "two", "three", "one", "three") 
arr: Array[String] = Array(one, one, two, three, one, three) 

scala> arr.groupBy(identity).mapValues(_.length) 
res0: scala.collection.immutable.Map[String,Int] = Map(one -> 3, three -> 2, two -> 1) 

更新:这不是效率比你的代码(我认为这是不可能击败),但它绝对是更具可读性。 groupBy method将所有相同的(因为我们使用identity)值汇总到一个Array,与仅增加计数相比,它有一些开销。

更新2:正如评论指出的那样,输出不可序列因为mapValues只是包装的地图并评估各get功能。您可以包裹地图上兑现地图由map(identity)

arr.groupBy(identity).mapValues(_.length).map(identity) 

,但它不是很漂亮的代码。

+0

谢谢! +1,以提高可读性。这种方式抛出一个''java.io.NotSerializableException''错误,虽然在Spark中做同样的事情。因此接受了替代答案。 – septra

6
arr.groupBy(identity).map{case (x,y) => x -> y.size} 
相关问题