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

현재 스칼라를 배우고 있으며 반전 된 값-> 키 조회를 수행하기 위해 맵을 반전해야합니다. 이 작업을 수행하는 간단한 방법을 찾고 있었지만 다음과 같은 방법으로 만 생각했습니다.

(Map() ++ origMap.map(kvp=>(kvp._2->kvp._1)))

누구보다 우아한 접근 방식이 있습니까?



답변

값이 고유하다고 가정하면 다음과 같이 작동합니다.

(Map() ++ origMap.map(_.swap))

그러나 Scala 2.8에서는 더 쉽습니다.

origMap.map(_.swap)

그렇게 할 수 있다는 것이 Scala 2.8에 새로운 컬렉션 라이브러리가있는 이유 중 하나입니다.


답변

에서 수학적으로, 매핑은, 예를 들어, 가역 (단사)하지 않을 수 있습니다 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))


답변

몇 가지 방법으로 반복하는 동안 ._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)

중복 값은 맵에 마지막으로 추가하면 덮어 씁니다.

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)