Programing

Scala에서지도를 반전시키는 우아한 방법

lottogame 2020. 8. 31. 08:20
반응형

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 Maps new groupMap method, which (as its name suggests) is an equivalent of a groupBy and a mapping over grouped items.

Map(1 -> "a", 2 -> "b", 4 -> "b").groupMap(_._2)(_._1)
// Map("b" -> List(2, 4), "a" -> List(1))

This:

  • groups elements based on their second tuple part (_._2) (group part of groupMap)

  • maps 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)).


  1. Inverse is a better name for this operation than reverse (as in "inverse of a mathematical function")

  2. 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

반응형