स्काला में नक्शे को पलटने का सुरुचिपूर्ण तरीका


97

वर्तमान में स्कैला सीखना और कुछ उल्टे मूल्य-> मुख्य लुकअप करने के लिए मानचित्र को उल्टा करने की आवश्यकता है। मैं यह करने के लिए एक सरल तरीका ढूंढ रहा था, लेकिन केवल साथ आया:

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

किसी को भी अधिक सुंदर दृष्टिकोण है?

जवाबों:


174

मान लेना अद्वितीय है, यह काम करता है:

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

स्कैला 2.8 पर, हालांकि, यह आसान है:

origMap.map(_.swap)

ऐसा करने में सक्षम होने के कारण स्काला 2.8 का नया संग्रह पुस्तकालय है।


1
सावधान! आप इस समाधान के साथ मूल्यों को ढीला कर सकते हैं। उदाहरण के लिए Map(1 -> "A", 2 -> "B", 3 -> "B").map(_.swap)परिणामMap(A -> 1, B -> 3)
dev-null

1
@ देव-नल मुझे क्षमा करें, लेकिन आपका उदाहरण आवश्यक धारणा के अंतर्गत नहीं आता है।
डैनियल सी। सोबरल

मैं सहमत हूँ। क्षमा करें, मैंने इसे अनदेखा कर दिया है।
देव-नल

45

गणितीय रूप से, मैपिंग इनवर्टेबल (इंजेक्टिव) नहीं हो सकती है, जैसे, से 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))

2
.map(_._1)अधिक लेगीबाइल के रूप में होगा.keys
cheezsteak

अब आपके लिए धन्यवाद, यहां तक ​​कि कुछ अक्षर भी छोटे हैं। अब अंतर यह है कि आप पहले Setकी तरह Listएस के बजाय एस हो जाते हैं।
रोक क्राल्ज

1
उपयोग करते समय सावधान रहें .mapValuesक्योंकि यह एक दृश्य लौटाता है। कभी-कभी, यह वही है जो आप चाहते हैं, लेकिन अगर आप सावधान नहीं हैं तो यह बहुत सारी मेमोरी और सीपीयू का उपभोग कर सकता है। यह एक नक्शे में बाध्य करने के लिए, आप कर सकते हैं m.groupBy(_._2).mapVaues(_.keys).map(identity), या आप के लिए कॉल की जगह सकता है .mapValues(_.keys)के साथ .map { case (k, v) => k -> v.keys }
मार्क टी।

10

कुछ तरीकों से पुनरावृत्ति करते हुए आप ._1 सामग्री से बच सकते हैं।

यहाँ एक तरीका है। यह एक आंशिक फ़ंक्शन का उपयोग करता है जो नक्शे के लिए एक और एकमात्र मामले को कवर करता है:

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

यहाँ एक और तरीका है:

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

नक्शा पुनरावृत्ति एक फ़ंक्शन को दो तत्व टपल के साथ कहता है, और अनाम फ़ंक्शन दो पैरामीटर चाहता है। Function.tupled अनुवाद बनाता है।


6

मैं यहां एक नक्शा प्रकार के नक्शे [A, Seq [B]] को 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))
}

जहां पुराना नक्शा प्रकार Map[A, Seq[B]]का होता है और नया नक्शा प्रकार का होता हैMap[B, Seq[A]]

नेस्टेड फोल्डफेट्स ने मुझे थोड़ा सा तंग कर दिया है, लेकिन यह सबसे सीधा तरीका है जो मुझे इस प्रकार के व्युत्क्रम को पूरा करने के लिए मिल सकता है। किसी के पास एक क्लीनर समाधान है?


बहुत अच्छा समाधान! : @Rok, उसके समाधान की तुलना में तुम्हारा थोड़ा मुझे लगता है कि क्योंकि वह बदल देती है किसी भी तरह अलग है Map[A, Seq[B]]करने के लिए Map[B, Seq[A]]जहां अपने समाधान trasnforms Map[A, Seq[B]]लिए Map[Seq[B], Seq[A]]
YH

1
उस मामले में, दो नेस्टेड सिलवटों के बिना और संभव अधिक प्रदर्शन करने वाला:a.toSeq.flatMap { case (a, b) => b.map(_ -> a) }.groupBy(_._2).mapValues(_.map(_._1))
रोक क्राल

6

ठीक है, इसलिए यह कई अच्छे उत्तरों के साथ एक बहुत पुराना सवाल है, लेकिन मैंने अंतिम, सभी-और-अंत-सभी, स्विस-आर्मी-चाकू, 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()

मैं एक ही निहित वर्ग में दोनों विधियों को रखना पसंद करूंगा, लेकिन जितना अधिक समय मैं इसे देखने में बिताऊंगा उतना अधिक समस्याग्रस्त दिखाई देगा।


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

1

Scala 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)

1

शुरू 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उनके दूसरे टपल भाग ( _._2) ( ग्रुप मैप का समूह भाग) पर आधारित तत्व

  • mapअपनी पहली टपल हिस्सा (लेने के आधार पर वर्गीकृत आइटम एस _._1) (के समूह हिस्सा नक्शा मानचित्र )

इसे वन-पास संस्करण के रूप में देखा जा सकता है map.groupBy(_._2).mapValues(_.map(_._1))


बहुत बुरा यह Map[K, C[V]]में तब्दील नहीं होगा Map[V, C[K]]
jwvh

0
  1. उलटा इस ऑपरेशन के लिए एक बेहतर नाम है, जैसे कि "एक गणितीय फ़ंक्शन का उलटा")

  2. मैं अक्सर यह उलटा रूपांतरण न केवल नक्शे पर बल्कि अन्य (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
    }

यदि यह एक-से-एक नक्शा है, तो आप सिंगलटन सूचियों के साथ समाप्त होते हैं, जिन्हें तुच्छ रूप से परीक्षण किया जा सकता है और नक्शे [बी, सूची [ए]] के बजाय मानचित्र [बी, ए] में तब्दील किया जा सकता है।


0

हम इस 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))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.