当前学习Scala并需要反转Map以执行一些反转值 - >键查找。我正在寻找一种简单的方法来做到这一点,但只提出了:
(Map() ++ origMap.map(kvp=>(kvp._2->kvp._1)))
有人有更优雅的方法吗?
答案 0 :(得分:160)
假设值是唯一的,这可行:
(Map() ++ origMap.map(_.swap))
但是,在Scala 2.8上,它更容易:
origMap.map(_.swap)
能够做到这一点是Scala 2.8有一个新的集合库的部分原因。
答案 1 :(得分:41)
在数学上,映射可能不是可逆的(单射),例如,来自Map[A,B]
,你无法得到Map[B,A]
,而是得到Map[B,Set[A]]
,因为可能会有所不同与相同值相关联的键。所以,如果你有兴趣知道所有的密钥,这里是代码:
scala> val m = Map(1 -> "a", 2 -> "b", 4 -> "b")
scala> m.groupBy(_._2).mapValues(_.keys)
res0: Map[String,Iterable[Int]] = Map(b -> Set(2, 4), a -> Set(1))
答案 2 :(得分:10)
你可以在几个方面进行迭代时避免使用._1的东西。
这是一种方式。这使用了一个部分函数,它涵盖了对地图重要的唯一一个案例:
Map() ++ (origMap map {case (k,v) => (v,k)})
这是另一种方式:
import Function.tupled
Map() ++ (origMap map tupled {(k,v) => (v,k)})
map迭代使用两个元素元组调用一个函数,而匿名函数需要两个参数。 Function.tupled进行翻译。
答案 3 :(得分:6)
我来到这里寻找一种方法将Map [A,Seq [B]]类型的Map反转为Map [B,Seq [A]],其中新地图中的每个B都与旧的地图,其中B包含在A的相关序列中。
如,
Map(1 -> Seq("a", "b"), 2-> Seq("b", "c"))
会颠倒到
Map("a" -> Seq(1), "b" -> Seq(1, 2), "c" -> Seq(2))
这是我的解决方案:
val newMap = oldMap.foldLeft(Map[B, Seq[A]]().withDefaultValue(Seq())) {
case (m, (a, bs)) => bs.foldLeft(m)((map, b) => map.updated(b, m(b) :+ a))
}
其中oldMap的类型为Map[A, Seq[B]]
,newMap的类型为Map[B, Seq[A]]
嵌套的foldLefts让我有点畏缩,但这是我能找到的最简单的方法来完成这种类型的反转。任何人都有更清洁的解决方案?
答案 4 :(得分:2)
您可以使用以下方式反转地图:
val i = origMap.map({case(k, v) => v -> k})
这种方法的问题在于,如果您的值(现在已成为地图中的哈希键)不是唯一的,那么您将删除重复值。举例说明:
scala> val m = Map("a" -> 1, "b" -> 2, "c" -> 3, "d" -> 1)
m: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3, d -> 1)
// Notice that 1 -> a is not in our inverted map
scala> val i = m.map({ case(k , v) => v -> k})
i: scala.collection.immutable.Map[Int,String] = Map(1 -> d, 2 -> b, 3 -> c)
为避免这种情况,您可以先将地图转换为元组列表,然后将其反转,这样就不会删除任何重复的值:
scala> val i = m.toList.map({ case(k , v) => v -> k})
i: List[(Int, String)] = List((1,a), (2,b), (3,c), (1,d))
答案 5 :(得分:2)
好的,所以这是一个非常老的问题,有很多好的答案,但是我建立了终极的,万事俱备的瑞士军刀Map
逆变器,这是发布它的地方。
实际上是两个逆变器。一个用于单个值元素...
//from Map[K,V] to Map[V,Set[K]], traverse the input only once
implicit class MapInverterA[K,V](m :Map[K,V]) {
def invert :Map[V,Set[K]] =
m.foldLeft(Map.empty[V, Set[K]]) {
case (acc,(k, v)) => acc + (v -> (acc.getOrElse(v,Set()) + k))
}
}
...以及另一个非常相似的价值收集方式。
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.Builder
import scala.language.higherKinds
//from Map[K,C[V]] to Map[V,C[K]], traverse the input only once
implicit class MapInverterB[K,V,C[_]](m :Map[K,C[V]]
)(implicit ev :C[V] => TraversableOnce[V]) {
def invert(implicit bf :CanBuildFrom[Nothing,K,C[K]]) :Map[V,C[K]] =
m.foldLeft(Map.empty[V, Builder[K,C[K]]]) {
case (acc, (k, vs)) =>
vs.foldLeft(acc) {
case (a, v) => a + (v -> (a.getOrElse(v,bf()) += k))
}
}.mapValues(_.result())
}
用法:
Map(2 -> Array('g','h'), 5 -> Array('g','y')).invert
//res0: Map(g -> Array(2, 5), h -> Array(2), y -> Array(5))
Map('q' -> 1.1F, 'b' -> 2.1F, 'c' -> 1.1F, 'g' -> 3F).invert
//res1: Map(1.1 -> Set(q, c), 2.1 -> Set(b), 3.0 -> Set(g))
Map(9 -> "this", 8 -> "that", 3 -> "thus", 2 -> "thus").invert
//res2: Map(this -> Set(9), that -> Set(8), thus -> Set(3, 2))
Map(1L -> Iterator(3,2), 5L -> Iterator(7,8,3)).invert
//res3: Map(3 -> Iterator(1, 5), 2 -> Iterator(1), 7 -> Iterator(5), 8 -> Iterator(5))
Map.empty[Unit,Boolean].invert
//res4: Map[Boolean,Set[Unit]] = Map()
我宁愿将这两种方法都放在同一个隐式类中,但是我花在研究它上的时间越多,它出现的问题就越多。
答案 6 :(得分:1)
在scala REPL中:
scala> val m = Map(1 -> "one", 2 -> "two")
m: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two)
scala> val reversedM = m map { case (k, v) => (v, k) }
reversedM: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2)
请注意,重复的值将被最后添加到地图中覆盖:
scala> val m = Map(1 -> "one", 2 -> "two", 3 -> "one")
m: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two, 3 -> one)
scala> val reversedM = m map { case (k, v) => (v, k) }
reversedM: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 3, two -> 2)
答案 7 :(得分:1)
从def create_optimizer(loss, init_lr, num_train_steps, num_warmup_steps):
"""Creates an optimizer training op."""
global_step = tf.train.get_or_create_global_step()
learning_rate = tf.constant(value=init_lr, shape=[], dtype=tf.float32)
# Implements linear decay of the learning rate.
learning_rate = tf.train.polynomial_decay(
learning_rate,
global_step,
num_train_steps,
end_learning_rate=0.0,
power=1.0,
cycle=False)
# Implements linear warmup. I.e., if global_step < num_warmup_steps, the
# learning rate will be `global_step/num_warmup_steps * init_lr`.
if num_warmup_steps:
global_steps_int = tf.cast(global_step, tf.int32)
warmup_steps_int = tf.constant(num_warmup_steps, dtype=tf.int32)
global_steps_float = tf.cast(global_steps_int, tf.float32)
warmup_steps_float = tf.cast(warmup_steps_int, tf.float32)
warmup_percent_done = global_steps_float / warmup_steps_float
warmup_learning_rate = init_lr * warmup_percent_done
is_warmup = tf.cast(global_steps_int < warmup_steps_int, tf.float32)
learning_rate = (
(1.0 - is_warmup) * learning_rate + is_warmup * warmup_learning_rate)
# It is recommended that you use this optimizer for fine tuning, since this
# is how the model was trained (note that the Adam m/v variables are NOT
# loaded from init_checkpoint.)
optimizer = AdamWeightDecayOptimizer(
learning_rate=learning_rate,
weight_decay_rate=0.01,
beta_1=0.9,
beta_2=0.999,
epsilon=1e-6)
tvars = tf.trainable_variables()
grads = tf.gradients(loss, tvars)
# You can do clip gradients if you need in this step(in general it is not neccessary)
# (grads, _) = tf.clip_by_global_norm(grads, clip_norm=1.0)
train_op = optimizer.apply_gradients(
zip(grads, tvars), global_step=global_step)
# Normally the global step update is done inside of `apply_gradients`.
# However, `AdamWeightDecayOptimizer` doesn't do this. But if you use
# a different optimizer, you should probably take this line out.
new_global_step = global_step + 1
train_op = tf.group(train_op, [global_step.assign(new_global_step)])
return train_op
开始,为了交换键/值而不丢失与相同值相关联的键,我们可以使用Scala 2.13
的新groupMap方法,该方法(顾名思义)是相当于Map
和groupBy
对分组项目的了解。
map
此:
Map(1 -> "a", 2 -> "b", 4 -> "b").groupMap(_._2)(_._1)
// Map("b" -> List(2, 4), "a" -> List(1))
的元素基于其第二元组部分(group
)(组地图的组部分)
_._2
的项目通过使用第一元组部分(map
)(组 Map 的地图部分)
这可以看作是_._1
中的one-pass version。
答案 8 :(得分:0)
对于此操作,反转是一个比反向更好的名称(如“数学函数的反转”)
我经常不仅在地图上而且在其他(包括Seq)集合上进行逆变换。我发现最好不要将我的逆操作的定义限制为一对一的映射。这是我为地图操作的定义(请建议改进我的实现)。
def invertMap[A,B]( m: Map[A,B] ) : Map[B,List[A]] = {
val k = ( ( m values ) toList ) distinct
val v = k map { e => ( ( m keys ) toList ) filter { x => m(x) == e } }
( k zip v ) toMap
}
如果它是一对一的地图,你最终得到的单例列表可以通过简单的测试并转换为Map [B,A]而不是Map [B,List [A]]。
答案 9 :(得分:0)
我们可以尝试使用这个foldLeft
函数来处理碰撞并在单次遍历中反转地图。
scala> def invertMap[A, B](inputMap: Map[A, B]): Map[B, List[A]] = {
| inputMap.foldLeft(Map[B, List[A]]()) {
| case (mapAccumulator, (value, key)) =>
| if (mapAccumulator.contains(key)) {
| mapAccumulator.updated(key, mapAccumulator(key) :+ value)
| } else {
| mapAccumulator.updated(key, List(value))
| }
| }
| }
invertMap: [A, B](inputMap: Map[A,B])Map[B,List[A]]
scala> val map = Map(1 -> 2, 2 -> 2, 3 -> 3, 4 -> 3, 5 -> 5)
map: scala.collection.immutable.Map[Int,Int] = Map(5 -> 5, 1 -> 2, 2 -> 2, 3 -> 3, 4 -> 3)
scala> invertMap(map)
res0: Map[Int,List[Int]] = Map(5 -> List(5), 2 -> List(1, 2), 3 -> List(3, 4))
scala> val map = Map("A" -> "A", "B" -> "A", "C" -> "C", "D" -> "C", "E" -> "E")
map: scala.collection.immutable.Map[String,String] = Map(E -> E, A -> A, B -> A, C -> C, D -> C)
scala> invertMap(map)
res1: Map[String,List[String]] = Map(E -> List(E), A -> List(A, B), C -> List(C, D))