斯卡拉:如何"映射"一个数组[Int]到一个Map [String,Int]使用" map"方法?

时间:2018-01-19 08:16:57

标签: scala functional-programming

我有以下Array[Int]val array = Array(1, 2, 3),我在Int和String之间有以下映射关系:

val a1 = array.map{
  case 1 => "A"
  case 2 => "B"
  case 3 => "C"
}

要创建Map以包含上述映射关系,我知道我可以使用foldLeft方法:

val a2 = array.foldLeft(Map[String, Int]()) { (m, e) =>
  m + (e match {
    case 1 => ("A", 1)
    case 2 => "B" -> 2
    case 3 => "C" -> 3
  })
}

输出:

  

a2:scala.collection.immutable.Map [String,Int] = Map(A - > 1,B - > 2,C    - > 3)

这是我想要的结果。 但是我可以通过map方法获得相同的结果吗?

以下代码不起作用:

val a3 = array.map[(String, Int), Map[String, Int]] {
  case 1 => ("A", 1)
  case 2 => ("B", 2)
  case 3 => ("C", 3)
}

map的签名是

def map[B, That](f: A => B)
(implicit bf: CanBuildFrom[Repr, B, That]): That

这是CanBuildFrom[Repr, B, That]是什么?我试着阅读Tribulations of CanBuildFrom,但并不理解。那篇文章提到Scala 2.12+为map提供了两个实现。但是当我使用Scala 2.12.4时,为什么我找不到它?

我主要使用Scala 2.11.12。

3 个答案:

答案 0 :(得分:3)

在表达式的末尾调用val a3 = array.map { case 1 => ("A", 1) case 2 => ("B", 2) case 3 => ("C", 3) }.toMap

query=Color:red,yellow,black AND Size:middle

答案 1 :(得分:2)

基本上toMap(信用Sergey Lagutin)是正确答案。

您实际上可以使代码更紧凑:

val a1 = array.map { i => ((i + 64).toChar, i) }.toMap

如果您运行此代码:

val array = Array(1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 0)
val a1 = array.map { i => ((i + 64).toChar, i) }.toMap
println(a1)

您将在控制台上看到这一点:

Map(E -> 5, J -> 10, F -> 6, A -> 1, @ -> 0, G -> 7, L -> 12, B -> 2, C -> 3, H -> 8, K -> 11, D -> 4)

答案 2 :(得分:2)

为了简洁起见,我先在这里定义你的功能:

// worth noting that this function is effectively partial
// i.e. will throw a `MatchError` if n is not in (1, 2, 3)
def toPairs(n: Int): (String, Int) =
  n match {
    case 1 => "a" -> 1
    case 2 => "b" -> 2
    case 3 => "c" -> 3
  }

一种可行的方法(正如另一个答案中已经突出显示的那样)是使用toMap,它只适用于对的集合:

val ns = Array(1, 2, 3)

ns.toMap // doesn't compile

ns.map(toPairs).toMap // does what you want

值得注意的是,除非您使用惰性表示(如IteratorStream),否则将导致对集合进行两次传递并创建不必要的中间集合:第一次将toPairs映射到集合上,然后将整个集合从一组对转变为MaptoMap)。

您可以在the implementation of toMap中清楚地看到它。

正如您在答案中已经链接的阅读中所建议的那样(特别是here)您可以通过两种方式避免这种双重传递:

  • 您可以利用scala.collection.breakOutCanBuildFrom的实现,您可以map(以及其他)更改目标集合,前提是您明确为编译器提供类型提示:
val resultMap: Map[String, Int] = ns.map(toPairs)(collection.breakOut)
val resultSet: Set[(String, Int)] = ns.map(toPairs)(collection.breakOut)
  • 否则,你可以在你的集合上创建一个视图,它将它放在你不需要导致双遍的操作所需的惰性包装器中
ns.view.map(toPairs).toMap

您可以在this Q&A中阅读有关隐式构建器提供程序和视图的更多信息。