Scala에서지도를 반전시키는 우아한 방법
현재 스칼라를 배우고 있으며 반전 된 값-> 키 조회를 수행하기 위해 맵을 반전해야합니다. 이 작업을 수행하는 간단한 방법을 찾고 있었지만 다음과 같은 방법으로 만 생각했습니다.
(Map() ++ origMap.map(kvp=>(kvp._2->kvp._1)))
누구보다 우아한 접근 방식이 있습니까?
값이 고유하다고 가정하면 다음과 같이 작동합니다.
(Map() ++ origMap.map(_.swap))
그러나 Scala 2.8에서는 더 쉽습니다.
origMap.map(_.swap)
그렇게 할 수 있다는 것이 Scala 2.8에 새로운 컬렉션 라이브러리가있는 이유 중 하나입니다.
수학적으로 매핑은 반전 불가능 (주 사적)이 아닐 수 있습니다 (예 : from Map[A,B]
, you ca n't get 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))
몇 가지 방법으로 반복하는 동안 ._1 항목을 피할 수 있습니다.
한 가지 방법이 있습니다. 이것은지도에 중요한 유일한 경우를 다루는 부분 함수를 사용합니다.
Map() ++ (origMap map {case (k,v) => (v,k)})
다른 방법이 있습니다.
import Function.tupled
Map() ++ (origMap map tupled {(k,v) => (v,k)})
맵 반복은 두 개의 요소 튜플이있는 함수를 호출하고 익명 함수는 두 개의 매개 변수를 원합니다. Function.tupled가 번역을합니다.
Map [A, Seq [B]] 유형의 Map을 Map [B, Seq [A]]로 전환하는 방법을 찾고 여기에 왔습니다. 여기서 새 맵의 각 B는 이전 맵의 모든 A와 연결됩니다. 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는 나를 약간 움츠 리게 만들지 만, 이것이 이러한 유형의 반전을 달성하기 위해 찾을 수있는 가장 간단한 방법입니다. 누구든지 깨끗한 솔루션이 있습니까?
좋아요,이 질문은 좋은 답변이 많은 아주 오래된 질문입니다.하지만 저는 궁극의 완전하고 끝없는 Swiss-Army-knife, 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()
동일한 암시 적 클래스에 두 메서드를 모두 사용하는 것을 선호하지만 더 많은 시간을 들여 조사할수록 문제가 더 많이 나타납니다.
다음을 사용하여지도를 반전 할 수 있습니다.
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))
스칼라 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)
Note that duplicate values will be overwritten by the last addition to the map:
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)
Starting Scala 2.13
, in order to swap key/values without loosing keys associated to same values, we can use Map
s new groupMap method, which (as its name suggests) is an equivalent of a groupBy
and a map
ping over grouped items.
Map(1 -> "a", 2 -> "b", 4 -> "b").groupMap(_._2)(_._1)
// Map("b" -> List(2, 4), "a" -> List(1))
This:
group
s elements based on their second tuple part (_._2
) (group part of groupMap)map
s grouped items by taking their first tuple part (_._1
) (map part of groupMap)
This can be seen as a one-pass version of map.groupBy(_._2).mapValues(_.map(_._1))
.
Inverse is a better name for this operation than reverse (as in "inverse of a mathematical function")
I often do this inverse transformation not only on maps but on other (including Seq) collections. I find it best not to limit the definition of my inverse operation to one-to-one maps. Here's the definition I operate with for maps (please suggest improvements to my implementation).
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 }
If it's a one-to-one map, you end up with singleton lists which can be trivially tested and transformed to a Map[B,A] rather than Map[B,List[A]].
We can try using this foldLeft
function that will take care of collisions and invert the map in single traversal.
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))
참고URL : https://stackoverflow.com/questions/2338282/elegant-way-to-invert-a-map-in-scala
'Programing' 카테고리의 다른 글
파이썬에서 0과 1 사이의 난수 (0) | 2020.08.31 |
---|---|
VS Code에서 "JSON에서 주석이 허용되지 않음"오류를 비활성화합니다. (0) | 2020.08.31 |
매크로가 아직 정의되지 않은 경우에만 정의하는 이유는 무엇입니까? (0) | 2020.08.31 |
angular2에서 타이머를 만드는 방법 (0) | 2020.08.31 |
Android에서 사용자 비활성을 감지하는 방법 (0) | 2020.08.31 |