স্কালায় কোনও মানচিত্র উল্টানোর জন্য মার্জিত উপায়


98

স্কেল শিখতে বর্তমানে কিছু উল্টানো মান-> কী লুপআপ করার জন্য মানচিত্রকে উল্টানো দরকার। আমি এটি করার জন্য একটি সহজ উপায়ের সন্ধান করছিলাম, তবে কেবলমাত্র এটি নিয়ে এসেছিল:

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

কারও বেশি মার্জিত অ্যাপ্রোচ আছে?

উত্তর:


176

ধরে নেওয়া মানগুলি অনন্য, এটি কাজ করে:

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

স্কেলা ২.৮ এ তবে এটি আরও সহজ:

origMap.map(_.swap)

এটি করতে সক্ষম হওয়া সেই কারণের একটি অংশ যা স্কাল ২.৮ একটি নতুন সংগ্রহ গ্রন্থাগার রয়েছে।


4
সাবধান! আপনি এই সমাধান সহ মূল্যবোধ শিথিল করতে পারেন। উদাহরণস্বরূপ Map(1 -> "A", 2 -> "B", 3 -> "B").map(_.swap)ফলাফলগুলির মধ্যেMap(A -> 1, B -> 3)
dev-null

4
@ দেব-নাল আমি দুঃখিত, তবে আপনার উদাহরণ প্রয়োজনীয় অনুমানের আওতায় পড়ে না।
ড্যানিয়েল সি সোব্রাল

আমি রাজী. দুঃখিত, আমি এটি উপেক্ষা করেছি।
dev-null

46

গাণিতিকভাবে, ম্যাপিংটি অবিচ্ছেদ্য (ইনজেকশন) নাও হতে পারে, যেমন, থেকে 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))

4
.map(_._1)আরও ন্যায়বিচারী হবে ঠিক.keys
চিজস্টেক

এখন আপনাকে ধন্যবাদ, এমনকি কয়েকটি অক্ষরও খাটো। পার্থক্যটি হ'ল আপনি আগের হিসাবে s Setএর পরিবর্তে Listএস পান।
রোক ক্রালজ

4
ব্যবহার করার সময় সতর্কতা অবলম্বন করুন .mapValuesকারণ এটি একটি দর্শন দেয়। মাঝে মধ্যে, আপনি যা চান এটি এটি তবে আপনি যদি সাবধান না হন তবে এটি প্রচুর মেমরি এবং সিপিইউ গ্রাস করতে পারে। এটিকে কোনও মানচিত্রে জোর করতে, আপনি করতে পারেন m.groupBy(_._2).mapVaues(_.keys).map(identity)বা আপনি কলটি এর .mapValues(_.keys)সাথে প্রতিস্থাপন করতে পারেন .map { case (k, v) => k -> v.keys }
টি।

.mapValues(_.keySet)একটি Iterable চেয়ে সেট পেতে বরং একটি ভালো বিকল্প
devyn

10

কয়েকটি উপায়ে পুনরাবৃত্তি করার সময় আপনি ._1 স্টাফ এড়াতে পারবেন।

এখানে একটি উপায়। এটি একটি আংশিক ফাংশন ব্যবহার করে যা মানচিত্রের জন্য গুরুত্বপূর্ণ এবং একমাত্র কেসটিকে কভার করে:

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

এখানে অন্য উপায়:

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

মানচিত্রের পুনরাবৃত্তি দুটি ফাংশনকে দুটি উপাদান টিপল সহ কল ​​করে এবং বেনামে ফাংশনটি দুটি পরামিতি চায়। Function.tupled অনুবাদ করে।


6

আমি এখানে মানচিত্র [এ, সিক [বি]] মানচিত্র [বি, সিক [এ]] তে পরিবর্তন করার জন্য একটি পথ সন্ধান করতে এসেছি, যেখানে নতুন মানচিত্রে প্রতিটি বি পুরানো মানচিত্রে প্রতিটি এ এর ​​সাথে যুক্ত রয়েছে যা বি এর সাথে সম্পর্কিত অনুক্রমের মধ্যে রয়েছে।

উদাহরণস্বরূপ, পরিবর্তন
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]]

নেস্টেড ফোল্ডেলফেল্টগুলি আমাকে কিছুটা কৃপণ করে তোলে, তবে এই ধরণের বিপরীতটি সম্পাদন করার পক্ষে এটি সবচেয়ে সহজ উপায় way কারও কি ক্লিনার সমাধান আছে?


খুব সুন্দর সমাধান! : @Rok তার সমাধান চেয়ে পুলিশের একটি বিট আমি মনে করি, কারণ তিনি রূপান্তরিত একরকম ভিন্ন Map[A, Seq[B]]করতে Map[B, Seq[A]]যেখানে আপনার সমাধান trasnforms Map[A, Seq[B]]করতে Map[Seq[B], Seq[A]]
ওয়াই এইচ

4
সেক্ষেত্রে দুটি নেস্টেড ভাঁজ এবং আরও বেশি পারফরম্যান্ট ছাড়াই: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> 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, একই মানগুলির সাথে সম্পর্কিত কীগুলি না হারিয়ে কী / মানগুলিকে অদলবদল করার জন্য, আমরা Maps এর নতুন গ্রুপম্যাপ পদ্ধতিটি ব্যবহার করতে পারি , যা (এর নাম অনুসারে প্রস্তাবিত) গোষ্ঠীযুক্ত আইটেমগুলির উপর একটি groupByএবং mapপিংয়ের সমতুল্য ।

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

এই:

  • groupতাদের দ্বিতীয় দ্বিখণ্ডিত অংশ ( _._2) ( গ্রুপ ম্যাপের গ্রুপ অংশ) এর উপর ভিত্তি করে এর উপাদানসমূহ

  • mapতাদের প্রথম tuple অংশ (গ্রহণ করে দলবদ্ধ আইটেম গুলি _._1) (গোষ্ঠীর অংশ ম্যাপ ম্যাপ )

এই হিসেবে দেখা যেতে পারে এক-পাস সংস্করণ এর map.groupBy(_._2).mapValues(_.map(_._1))


খুব খারাপ, এটা রুপান্তর করা হবে না Map[K, C[V]]মধ্যে Map[V, C[K]]
jwvh

0
  1. বিপরীতমুখী ("গাণিতিক ক্রিয়াটির বিপরীতে" হিসাবে) এই ক্রিয়াকলাপের পক্ষে বিপরীত একটি ভাল নাম)

  2. আমি প্রায়শই কেবল মানচিত্রগুলিতে নয় অন্য অন্যান্য (সিক সহ) সংগ্রহগুলিতেও এই বিপরীত রূপান্তরটি করি। আমার বিপরীত ক্রিয়াকলাপের সংজ্ঞাটি এক-এক-এক মানচিত্রের মধ্যে সীমাবদ্ধ না রাখাই ভাল। মানচিত্রের জন্য আমি যে সংজ্ঞাটি দিয়েছি তা এখানে রয়েছে (দয়া করে আমার প্রয়োগের উন্নতির পরামর্শ দিন)।

    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))
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.