简体   繁体   English

在 Scala 中平面映射嵌套的 Map

[英]flatmapping a nested Map in scala

Suppose I have val someMap = Map[String -> Map[String -> String]] defined as such:假设我有val someMap = Map[String -> Map[String -> String]]定义如下:

val someMap = 
Map( 
    ("a1" -> Map( ("b1" -> "c1"), ("b2" -> "c2") ) ),
    ("a2" -> Map( ("b3" -> "c3"), ("b4" -> "c4") ) ),
    ("a3" -> Map( ("b5" -> "c5"), ("b6" -> "c6") ) )   
)

and I would like to flatten it to something that looks like我想把它压扁成看起来像的东西

List(   
    ("a1","b1","c1"),("a1","b2","c2"),
    ("a2","b3","c3"),("a2","b4","c4"),
    ("a3","b5","c5"),("a3","b6","c6")
)

What is the most efficient way of doing this?这样做的最有效方法是什么? I was thinking about creating some helper function that processes each (a_i -> Map(String,String)) key value pair and return我正在考虑创建一些辅助函数来处理每个(a_i -> Map(String,String))键值对并返回

def helper(key: String, values: Map[String -> String]): (String,String,String) 
= {val sublist = values.map(x => (key,x._1,x._2))
return sublist
}

then flatmap this function over someMap .然后将该函数someMap映射到someMap But this seems somewhat unnecessary to my novice scala eyes, so I was wondering if there was a more efficient way to parse this Map.但这对我的 scala 新手来说似乎有些不必要,所以我想知道是否有更有效的方法来解析这个 Map。

No need to create helper function just write nested lambda:无需创建辅助函数,只需编写嵌套的 lambda:

val result = someMap.flatMap { case (k, v) => v.map { case (k1, v1) => (k, k1, v1) } }

Or或者

val y = someMap.flatMap(x => x._2.map(y => (x._1, y._1, y._2)))

Since you're asking about efficiency, the most efficient yet functional approach I can think of is using foldLeft and foldRight .既然您问的是效率,我能想到的最有效但最实用的方法是使用foldLeftfoldRight

You need foldRight since :: constructs the immutable list in reverse.您需要foldRight因为::反向构造不可变列表。

someMap.foldRight(List.empty[(String, String, String)]) { case ((a, m), acc)  =>
  m.foldRight(acc) {
    case ((b, c), acc) => (a, b, c) :: acc
  }
}

Here, assuming Map.iterator.reverse is implemented efficiently, no intermediate collections are created.在这里,假设Map.iterator.reverse被有效地实现,则不会创建中间集合。

Alternatively, you can use foldLeft and then reverse the result:或者,您可以使用foldLeft然后reverse结果:

someMap.foldLeft(List.empty[(String, String, String)]) { case (acc, (a, m))  =>
  m.foldLeft(acc) {
    case (acc, (b, c)) => (a, b, c) :: acc
  }
}.reverse

This way a single intermediate List is created, but you don't rely on the implementation of the reversed iterator ( foldLeft uses forward iterator).这样就创建了一个中间List ,但您不依赖于反向迭代器的实现( foldLeft使用正向迭代器)。


Note: one liners, such as someMap.flatMap(x => x._2.map(y => (x._1, y._1, y._2))) are less efficient, as, in addition to the temporary buffer to hold intermediate results of flatMap , they create and discard additional intermediate collections for each inner map .注意:一种衬垫,例如someMap.flatMap(x => x._2.map(y => (x._1, y._1, y._2)))效率较低,因为除了临时缓冲区为了保存flatMap中间结果,它们为每个内部map创建和丢弃额外的中间集合。


UPD UPD

Since there seems to be some confusion, I'll clarify what I mean.由于似乎有些混乱,我将澄清我的意思。 Here is an implementation of map , flatMap , foldLeft and foldRight from TraversibleLike :这是来自TraversibleLikemapflatMapfoldLeftfoldRight的实现:

  def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    def builder = { // extracted to keep method size under 35 bytes, so that it can be JIT-inlined
      val b = bf(repr)
      b.sizeHint(this)
      b
    }
    val b = builder
    for (x <- this) b += f(x)
    b.result
  }

  def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    def builder = bf(repr) // extracted to keep method size under 35 bytes, so that it can be JIT-inlined
    val b = builder
    for (x <- this) b ++= f(x).seq
    b.result
  }

  def foldLeft[B](z: B)(op: (B, A) => B): B = {
    var result = z
    this foreach (x => result = op(result, x))
    result
  }

  def foldRight[B](z: B)(op: (A, B) => B): B =
    reversed.foldLeft(z)((x, y) => op(y, x))

It's clear that map and flatMap create intermediate buffer using corresponding builder , while foldLeft and foldRight reuse the same user-supplied accumulator object, and only use iterators.很明显, mapflatMap使用相应的builder创建中间缓冲区,而foldLeftfoldRight重用相同的用户提供的累加器对象,并且只使用迭代器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM