Відповіді:
Якщо припустити, що значення унікальні, це працює:
(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))
.map(_._1)
було б більш розбірливим як раз.keys
Set
s замість List
s, як раніше.
.mapValues
оскільки це повертає вигляд. Іноді, це те, що ви хочете, але якщо ви не будете обережні, це може зайняти багато пам'яті та процесора. Для того, щоб змусити його в карту, ви можете зробити m.groupBy(_._2).mapVaues(_.keys).map(identity)
, або ви могли б замінити виклик .mapValues(_.keys)
з .map { case (k, v) => k -> v.keys }
.
Ви можете уникнути ._1 матеріалів під час повторення кількома способами.
Ось один із способів. Для цього використовується часткова функція, яка охоплює єдиний випадок, який має значення для карти:
Map() ++ (origMap map {case (k,v) => (v,k)})
Ось ще один спосіб:
import Function.tupled
Map() ++ (origMap map tupled {(k,v) => (v,k)})
Ітерація карти викликає функцію з двома елементами, а анонімна функція хоче двох параметрів. Функція.tupled робить переклад.
Я прийшов сюди, шукаючи спосіб перетворити карту типу Map [A, Seq [B]] на Map [B, Seq [A]], де кожен B на новій карті асоціюється з кожним A на старій карті для який B містився в асоційованій послідовності А.
Наприклад,
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]]
Вкладені складчасті ліфти змушують мене трохи скупитися, але це найпростіший спосіб, який я міг би знайти для здійснення цього типу інверсії. У кого є чистіший розчин?
Map[A, Seq[B]]
до Map[B, Seq[A]]
де ваші trasnforms рішення Map[A, Seq[B]]
для Map[Seq[B], Seq[A]]
.
a.toSeq.flatMap { case (a, b) => b.map(_ -> a) }.groupBy(_._2).mapValues(_.map(_._1))
Гаразд, тож це дуже старе питання з багатьма хорошими відповідями, але я створив остаточний, 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)
Починаючи Scala 2.13
, для того, щоб поміняти ключ / значення без втрати ключів, пов'язаних з тими самими значеннями, ми можемо використовувати Map
новий метод groupMap , який (як випливає з назви) є еквівалентом а groupBy
та map
пінг над згрупованими елементами.
Map(1 -> "a", 2 -> "b", 4 -> "b").groupMap(_._2)(_._1)
// Map("b" -> List(2, 4), "a" -> List(1))
Це:
group
s елементів на основі їх другої кортежної частини ( _._2
) (групова частина групової карти)
map
s групує елементи, беручи їхню першу кортежну частину ( _._1
) (карта частини групи Map )
Це можна розглядати як варіант однопрохідної з map.groupBy(_._2).mapValues(_.map(_._1))
.
Map[K, C[V]]
на Map[V, C[K]]
.
Inverse - краща назва цієї операції, ніж зворотний (як у "оберненій математичній функції")
Я часто роблю це обернене перетворення не тільки на картах, але й на інших (включаючи 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
}
Якщо це карта "один на один", ви отримуєте списки однотонних, які можна тривіально перевірити і перетворити на карту [B, A], а не Map [B, List [A]].
Ми можемо спробувати скористатися цією 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))
Map(1 -> "A", 2 -> "B", 3 -> "B").map(_.swap)
результати вMap(A -> 1, B -> 3)