103

現在、Scala を学習しており、Map を反転して反転値 -> キー ルックアップを行う必要がありました。これを行う簡単な方法を探していましたが、思いついたのは次のとおりです。

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

誰もがよりエレガントなアプローチを持っていますか?

4

10 に答える 10

186

値が一意であると仮定すると、これは機能します。

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

ただし、Scala 2.8 ではより簡単です。

origMap.map(_.swap)

それができるのは、Scala 2.8 に新しいコレクション ライブラリがある理由の 1 つです。

于 2010-02-25T23:59:57.260 に答える
49

数学的には、マッピングは可逆 (単射) ではない場合があります。たとえば、 からMap[A,B]を取得することはできませんが、同じ値に関連付けられた異なるキーが存在する可能性があるため、Map[B,A]を取得することはできません。Map[B,Set[A]]したがって、すべてのキーを知りたい場合は、コードを次に示します。

val m = Map(1 -> "a", 2 -> "b", 4 -> "b")
m.groupBy(_._2).mapValues(_.keys)

res0: Map[String,Iterable[Int]] = Map(b -> Set(2, 4), a -> Set(1))

scala 2.13 から、これはさらにシンプルで効率的になります。

m.groupMap(_._2)(_._1)
于 2014-06-14T17:01:06.823 に答える
10

いくつかの方法で反復しながら ._1 のものを避けることができます。

これが1つの方法です。これは、マップにとって重要な唯一のケースをカバーする部分関数を使用します。

Map() ++ (origMap map {case (k,v) => (v,k)})

別の方法は次のとおりです。

import Function.tupled        
Map() ++ (origMap map tupled {(k,v) => (v,k)})

map 反復は 2 つの要素のタプルを持つ関数を呼び出し、無名関数は 2 つのパラメーターを必要とします。Function.tupled が翻訳を行います。

于 2010-02-25T23:24:40.370 に答える
7

OK、これは多くの良い答えがある非常に古い質問ですが、私は究極の万能のスイスアーミーナイフMapインバーターを構築しました。これは投稿する場所です.

実際には2つのインバーターです。個々の値要素用の 1 つ...

//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()

私は両方のメソッドを同じ暗黙のクラスに入れたいと思っていますが、調べるのに時間を費やすほど、問題が発生するようになりました。

于 2018-07-16T07:19:18.393 に答える
6

私は、タイプ 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 には少しうんざりしますが、これは、このタイプの反転を達成するために見つけた最も簡単な方法です。誰もがよりクリーンなソリューションを持っていますか?

于 2013-11-05T23:15:48.237 に答える
3

次を使用してマップを反転できます。

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))
于 2014-11-14T02:50:52.140 に答える
1

スカラ 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)
于 2013-10-09T00:14:37.117 に答える
0
  1. 逆は、この操作の逆よりも適切な名前です (「数学関数の逆」など)。

  2. この逆変換は、マップだけでなく、他の (Seq を含む) コレクションでもよく行います。逆演算の定義を 1 対 1 のマップに限定しないのが最善だと思います。これが私がマップで操作する定義です(実装の改善を提案してください)。

    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
    }
    

1 対 1 のマップの場合、Map[B,List[A]] ではなく Map[B,A] に簡単にテストして変換できるシングルトン リストになります。

于 2012-05-19T09:26:26.160 に答える