দুটি মানচিত্র একত্রিত করার এবং একই কী এর মানগুলি যোগ করার সর্বোত্তম উপায়?


179
val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)

আমি তাদের মার্জ করতে এবং একই কীগুলির মানগুলি যোগ করতে চাই। সুতরাং ফলাফল হবে:

Map(2->20, 1->109, 3->300)

এখন আমার 2 টি সমাধান রয়েছে:

val list = map1.toList ++ map2.toList
val merged = list.groupBy ( _._1) .map { case (k,v) => k -> v.map(_._2).sum }

এবং

val merged = (map1 /: map2) { case (map, (k,v)) =>
    map + ( k -> (v + map.getOrElse(k, 0)) )
}

তবে আমি আরও ভাল সমাধান আছে কিনা জানতে চাই।


সহজতমmap1 ++ map2
সেরাফ

3
@ সেরাফ যা মানচিত্রের মানগুলি সংখ্যার পরিবর্তে ডুপ্লিকেটগুলি উপেক্ষা করে কেবল মানচিত্রগুলিকে কেবল "মার্জ" করে।
জেনিপ আক্ক্যালিওঙ্কু ইলমাজ

@ জিনেপএকল্যাওনকিউ ইলমাজ ডান প্রশ্নটি আরও ভাল করে পড়া উচিত ছিল, লজ্জায় ফেলে
Seraf

উত্তর:


143

স্কালাজের একটি সেমিগ্রুপের ধারণা রয়েছে যা আপনি এখানে যা করতে চান তা ক্যাপচার করে এবং তর্কাতীতভাবে সবচেয়ে সংক্ষিপ্ত / পরিষ্কার সমাধানের দিকে নিয়ে যায়:

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> val map1 = Map(1 -> 9 , 2 -> 20)
map1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 9, 2 -> 20)

scala> val map2 = Map(1 -> 100, 3 -> 300)
map2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 100, 3 -> 300)

scala> map1 |+| map2
res2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 109, 3 -> 300, 2 -> 20)

বিশেষত, বাইনারি অপারেটরের জন্য Map[K, V]মানচিত্রের কীগুলি একত্রিত করে, Vকোনও ডুপ্লিকেট মানগুলির চেয়ে ভাঁজ করে দেওয়া সেমিগ্রুপ অপারেটর। Intসংযোজন অপারেটরের ব্যবহারের জন্য স্ট্যান্ডার্ড সেমিগ্রুপ , সুতরাং আপনি প্রতিটি নকল কীটির জন্য মানের যোগফল পাবেন।

সম্পাদনা : ব্যবহারকারীর অনুরোধ অনুসারে আরও কিছু বিশদ।

গাণিতিকভাবে একটি সেমিগ্রুপ হ'ল মানগুলির একটি সেট, এক অপারেটরের সাথে যা সে সেট থেকে দুটি মান নেয় এবং সে সেট থেকে অন্য মান উৎপন্ন করে। সুতরাং সংযোজনের অধীনে পূর্ণসংখ্যাগুলি একটি আধাগোষ্ঠী হয়, উদাহরণস্বরূপ - +অপারেটর দুটি আরও অন্তর্নির্মিত করতে দুটি ইন্ট যুক্ত করে।

আপনি "প্রদত্ত মূল প্রকার এবং মান ধরণের সমস্ত মানচিত্র" এর সেটগুলির উপরও একটি আধাগোষ্ঠী সংজ্ঞায়িত করতে পারেন, যতক্ষণ আপনি কিছু ক্রিয়াকলাপ নিয়ে আসতে পারেন যা দুটি মানচিত্রকে একত্রিত করে একটি নতুন উত্পাদন করতে পারে যা কোনওভাবে দুটির সংমিশ্রণ রয়েছে ইনপুট।

যদি উভয় মানচিত্রে কোনও কী উপস্থিত না হয় তবে এটি তুচ্ছ। যদি উভয় মানচিত্রে একই কী বিদ্যমান থাকে তবে আমাদের কী মানচিত্রগুলিতে দুটি মান একত্রিত করতে হবে। হুম, আমরা কেবল এমন কোনও অপারেটর বর্ণনা করিনি যা একই ধরণের দুটি সত্তাকে সংযুক্ত করে? এ কারণেই স্কালাজে একটি আধাগোষ্ঠীMap[K, V] উপস্থিত রয়েছে এবং কেবল যদি উপস্থিত Vথাকে - Vএর সেমিগ্রুপ দুটি মানচিত্র থেকে মানগুলি একত্রিত করতে ব্যবহৃত হয় যা একই কীতে বরাদ্দ করা হয়।

সুতরাং কারণ Int এখানে মানের ধরণটি 1হ'ল , কীটির "সংঘর্ষ" দুটি ম্যাপযুক্ত মানের পূর্ণসংখ্যা যোগ করার মাধ্যমে সমাধান করা হয়েছে (যেমন এটি ইন্টের সেমিগ্রুপ অপারেটর করে), তাই 100 + 9। মানগুলি যদি স্ট্রিংগুলি হয় তবে একটি সংঘর্ষের ফলে দুটি ম্যাপযুক্ত মানগুলি স্ট্রিং কনটেন্টেশন হতে পারে (আবার, কারণ স্ট্রিংয়ের জন্য সেমিগ্রুপ অপারেটর এটিই করে)।

(এবং মজার বিষয় হল যেহেতু স্ট্রিং কনটেনটেশন কমিটিকেটিভ নয় - অর্থাৎ "a" + "b" != "b" + "a"- ফলস্বরূপ সেমিগ্রুপ অপারেশন হয় না So স্ট্রিংয়ের ক্ষেত্রে map1 |+| map2এটি পৃথক map2 |+| map1, তবে ইন্টার ক্ষেত্রে নয়))


37
উজ্জ্বল! প্রথম ব্যবহারিক উদাহরণ যেখানে scalazজ্ঞান তৈরি হয়েছিল।
soc

5
দুষ্টুমি করসি না! যদি আপনি এটি সন্ধান শুরু করেন ... এটি পুরো জায়গা জুড়ে। চশমা এবং চশমা 2 এর ত্রুটিযুক্ত টরবোন লেখককে উদ্ধৃত করার জন্য: "প্রথমে আপনি বিকল্পটি শিখেন এবং আপনি এটি সর্বত্র দেখতে শুরু করেন Then তারপরে আপনি আবেদনকারী শিখেন এবং এটি একই জিনিস Next পরবর্তী?" এরপরে আরও কার্যকরী ধারণা রয়েছে। এবং এগুলি আপনাকে আপনার কোডটি গঠনে এবং সমস্যাগুলি সুন্দরভাবে সমাধানে সহায়তা করে।
AndreasSchainert

4
আসলে, আমি পাঁচ বছর ধরে অপশনটির সন্ধান করছিলাম যখন শেষ পর্যন্ত স্কেলাটি পেলাম। জাভা অবজেক্টের রেফারেন্সের মধ্যে পার্থক্য যা নাল হতে পারে এবং যেটি হতে পারে না (যেমন Aএবং এর মধ্যে Option[A]) এত বিশাল, আমি বিশ্বাস করতে পারি না যে তারা সত্যই একই ধরণের ছিল। আমি সবে স্ক্যালাজের দিকে তাকাতে শুরু করলাম। আমি নিশ্চিত নই আমি যথেষ্ট স্মার্ট ...
মালভোলিও

1
জাভার জন্যও অপশন রয়েছে, ফাংশনাল জাভা দেখুন। কোনও ভয় নেই, শেখা মজাদার। এবং ফাংশনাল প্রোগ্রামিং আপনাকে নতুন জিনিস (কেবল) শেখায় না বরং এর পরিবর্তে সমস্যাগুলি মোকাবেলায় শর্তাদি, শব্দভাণ্ডার সরবরাহ করতে আপনাকে প্রোগ্রামার সহায়তা দেয়। ওপি প্রশ্ন একটি নিখুঁত উদাহরণ। একটি সেমিগ্রুপের ধারণাটি এত সহজ, আপনি প্রতিদিন এটি ব্যবহার করেন উদাহরণস্বরূপ স্ট্রিংস হিসাবে। আসল শক্তিটি উপস্থিত হয় যদি আপনি এই বিমূর্তিটি সনাক্ত করেন, নাম দিন এবং শেষ পর্যন্ত অন্য ধরণের ক্ষেত্রে প্রয়োগ করুন তবে কেবল স্ট্রিং।
AndreasSchainert

1
এটি কীভাবে সম্ভব যে এর ফলাফল 1 -> (100 + 9) এ আসবে? আপনি কি আমাকে "স্ট্যাক ট্রেস" দেখাতে পারেন? ধন্যবাদ. পিএস: আমি এখানে আরও স্পষ্টভাবে উত্তর দিতে বলছি।
ব্যবহারকারী 482745

152

আমি যেটি জানি তার সংক্ষিপ্ত উত্তরটি কেবলমাত্র স্ট্যান্ডার্ড লাইব্রেরি ব্যবহার করে

map1 ++ map2.map{ case (k,v) => k -> (v + map1.getOrElse(k,0)) }

34
সুন্দর সমাধান। আমি ইঙ্গিতটি যুক্ত করতে চাই, যা ডান পাশের মানচিত্র থেকে (কে, ভি) দ্বারা (কে, ভি) দ্বারা (কে, ভি) ইতিমধ্যে বামে উপস্থিত থাকলে , যে ++কোনও (কে, ভি) প্রতিস্থাপন ++করে পাশের মানচিত্র (এখানে মানচিত্র 1), যেমনMap(1->1) ++ Map(1->2) results in Map(1->2)
লুৎজ

এক ধরণের নীচু সংস্করণ: ((কে, ভি) <- (এএ ++ বিবি)) ফলনের জন্য কে -> (যদি ((এএএতে কে থাকে) && (বিবিতে কে রয়েছে)) আ (কে) + ভি অন্য ভি)
ডিভাইডবাইজারো

আমি এর আগে কিছুটা আলাদা করেছি, তবে আপনি এখানে যা করেছেন তার একটি সংস্করণ এখানে একটি মানচিত্র for1++ (((কে, ভি) <- ম্যাপ 2) এর জন্য ফলন কে -> (v + map1.getOrElse (কে, 0 )))
ডিভাইডবাইজারো

1
@ জাস 12 - এর .চেয়ে বেশি প্রাধান্য রয়েছে ++; আপনি map1 ++ map2.map{...}হিসাবে পড়া map1 ++ (map2 map {...})। সুতরাং এক উপায়ে আপনি map1গুলি এর উপাদানগুলিকে মানচিত্র করুন এবং অন্য উপায়ে আপনি করবেন না।
রেক্স কের

1
@ ম্যাট - স্কালাজ ইতিমধ্যে এটি করবে, তাই আমি বলব "একটি বিদ্যমান গ্রন্থাগার ইতিমধ্যে এটি করেছে"।
রেক্স কের


41

ঠিক আছে, এখন স্কেলা লাইব্রেরিতে (কমপক্ষে 2.10 এ) আপনার পছন্দের কিছু রয়েছে - মার্জ করা ফাংশন। তবে এটি কেবল মানচিত্রে নয় হ্যাশম্যাপে উপস্থাপন করা হয়েছে। এটা কিছুটা বিভ্রান্তিকর। এছাড়াও স্বাক্ষরটি জটিল - আপনি দু'বার কী কী প্রয়োজন এবং কখন অন্য কী দিয়ে একটি জুড়ি তৈরি করতে হবে তা ভাবতে পারেন না। তবে তবুও, এটি কাজ করে এবং পূর্ববর্তী "নেটিভ" সমাধানগুলির চেয়ে অনেক পরিষ্কার।

val map1 = collection.immutable.HashMap(1 -> 11 , 2 -> 12)
val map2 = collection.immutable.HashMap(1 -> 11 , 2 -> 12)
map1.merged(map2)({ case ((k,v1),(_,v2)) => (k,v1+v2) })

এছাড়াও স্ক্যালাডোক উল্লেখ করেছেন

mergedপদ্ধতির উপর একটি ট্র্যাভেরসাল করছেন এবং স্ক্র্যাচ থেকে একটি নতুন অপরিবর্তনীয় হ্যাশ মানচিত্র পুনর্গঠন, বা তুলনায় গড় আরো performant হয় ++


1
এই মুহূর্তে, এটি কেবল পরিবর্তনযোগ্য হাশম্যাপে রয়েছে, পরিবর্তনীয় হাশম্যাপে নয়।
কেভিন হুইলার

2
এটি বেশ বিরক্তিকর যে তাদের কাছে কেবল হ্যাশম্যাপগুলি সৎ হতে পারে।
জোহান এস

আমি এটি সংকলন করতে পারছি না, এটি ব্যক্তিগত ধরণের হিসাবে গ্রহণযোগ্যতার মতো বলে মনে হচ্ছে, তাই আমি টাইপ করা কোনও ফাংশনে মেলাতে পারছি না।
রায়ান দি লিচ

2
2.11 সংস্করণে কিছু পরিবর্তন হয়েছে বলে মনে হচ্ছে। 2.10 স্ক্যালাডোক দেখুন - স্কেলা-lang.org/api/2.10.1/… একটি সাধারণ ক্রিয়াকলাপ রয়েছে। তবে ২.১১-এ এটি MergeFunction
মিখাইল গোলুবতসভ

২.১১-এ পরিবর্তিত সমস্ত হ'ল এই নির্দিষ্ট ফাংশনের ধরণের জন্য একটি টাইপ private type MergeFunction[A1, B1] = ((A1, B1), (A1, B1)) => (A1, B1)
ওরফেটির পরিচিতি

14

এটি কেবল প্লেইন স্কালার সাথে মনোয়েড হিসাবে প্রয়োগ করা যেতে পারে । এখানে একটি নমুনা বাস্তবায়ন। এই পদ্ধতির সাহায্যে আমরা 2 টি নয়, মানচিত্রের তালিকাটি মার্জ করতে পারি।

// Monoid trait

trait Monoid[M] {
  def zero: M
  def op(a: M, b: M): M
}

দুটি মানচিত্রকে একত্রিত করে মনোয়েড বৈশিষ্ট্যের মানচিত্র ভিত্তিক প্রয়োগ।

val mapMonoid = new Monoid[Map[Int, Int]] {
  override def zero: Map[Int, Int] = Map()

  override def op(a: Map[Int, Int], b: Map[Int, Int]): Map[Int, Int] =
    (a.keySet ++ b.keySet) map { k => 
      (k, a.getOrElse(k, 0) + b.getOrElse(k, 0))
    } toMap
}

এখন, যদি আপনার কাছে মানচিত্রের একত্রীকরণের প্রয়োজনের একটি তালিকা থাকে (এই ক্ষেত্রে কেবল 2), এটি নীচের মতো করা যেতে পারে।

val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)

val maps = List(map1, map2) // The list can have more maps.

val merged = maps.foldLeft(mapMonoid.zero)(mapMonoid.op)


5

আমি এই সম্পর্কে একটি ব্লগ পোস্ট লিখেছি, এটি দেখুন:

http://www.nimrodstech.com/scala-map-merge/

মূলত স্কালাজ আধা গ্রুপ ব্যবহার করে আপনি এটি খুব সহজেই অর্জন করতে পারেন

কিছু দেখতে হবে:

  import scalaz.Scalaz._
  map1 |+| map2

11
আপনার উত্তরে আপনাকে আরও কিছুটা বিশদ রাখতে হবে, সম্ভবত কিছু বাস্তবায়ন কোড। আপনার পোস্ট করা অন্যান্য অনুরূপ উত্তরের জন্যও এটি করুন, এবং নির্দিষ্ট প্রশ্নের প্রতিটি উত্তর শিখিয়েছেন। থাম্বের বিধি: প্রশ্নকারীর ব্লগ লিঙ্কটি ক্লিক না করে আপনার উত্তরটি থেকে উপকৃত হওয়া উচিত।
রবার্ট হার্ভে

5

আপনি বিড়ালদের সাথে এটি করতে পারেন ।

import cats.implicits._

val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)

map1 combine map2 // Map(2 -> 20, 1 -> 109, 3 -> 300)

Eek import cats.implicits._,। import cats.instances.map._ import cats.instances.int._ import cats.syntax.semigroup._খুব বেশি
ভার্বোজ

@ সেন্ট অ্যান্টারিওতে কেবল এটির প্রস্তাব দেওয়া উপায়import cats.implicits._
আর্টসিয়াম মিক্লুশৌ

কার কাছ থেকে পুনরুদ্ধার? সমস্ত (যার মধ্যে বেশিরভাগ অব্যবহৃত) অন্তর্নিহিত দৃষ্টান্তগুলি স্কোপে আনা কম্পাইলারের জীবনকে জটিল করে তোলে। এবং এছাড়াও যদি কারও প্রয়োজন না হয়, বলুন, প্রয়োগমূলক উদাহরণ কেন তারা এটিকে সেখানে আনবেন?
সেন্ট অ্যান্টারিও

4

শুরু করা Scala 2.13, কেবলমাত্র স্ট্যান্ডার্ড লাইব্রেরির উপর ভিত্তি করে অন্য একটি সমাধান groupByআপনার সমাধানের অংশটি প্রতিস্থাপনের সাথে অন্তর্ভুক্ত করে groupMapReduce(যার নাম অনুসারে এটি প্রস্তাবিত হয়) এর groupByপরে mapValuesএবং একটি হ্রাস পদক্ষেপের সমতুল্য :

// val map1 = Map(1 -> 9, 2 -> 20)
// val map2 = Map(1 -> 100, 3 -> 300)
(map1.toSeq ++ map2).groupMapReduce(_._1)(_._2)(_+_)
// Map[Int,Int] = Map(2 -> 20, 1 -> 109, 3 -> 300)

এই:

  • টিপলস ( List((1,9), (2,20), (1,100), (3,300))) এর ক্রম হিসাবে দুটি মানচিত্রকে সংযুক্ত করে । সংক্ষিপ্তকরনের জন্য, map2হয় পরোক্ষভাবে রূপান্তরিত Seqধরণ মানিয়ে নিতে map1.toSeq- কিন্তু আপনি এটি ব্যবহার দ্বারা স্পষ্ট হিসাবে চয়ন করতে পারেন map2.toSeq,

  • groupতাদের প্রথম টিউপল অংশের উপর ভিত্তি করে উপাদানগুলি ( গ্রুপ ম্যাপ্রেডিউসের গ্রুপ অংশ ),

  • mapএর দ্বিতীয় টিউপল অংশে গ্রুপের মানগুলি (গ্রুপের মানচিত্র হ্রাসের মানচিত্র অংশ ),

  • reduce_+_ম্যাপযুক্ত মানগুলি ( ) তাদের যোগ করে (গ্রুপম্যাপ হ্রাসের অংশ হ্রাস করুন )।


3

এখানে আমি যা ব্যবহার করে শেষ করেছি:

(a.toSeq ++ b.toSeq).groupBy(_._1).mapValues(_.map(_._2).sum)

1
ওপি প্রস্তাবিত প্রথম সমাধানের চেয়ে এটি সত্যিই আলাদা নয়।
jwvh

2

আন্ড্রেজে ডয়েলের উত্তরে সেমিগ্রুপগুলির দুর্দান্ত ব্যাখ্যা রয়েছে যা আপনাকে |+|অপারেটরটিকে দুটি মানচিত্রে যোগ দিতে এবং মেলানো কীগুলির জন্য মানগুলি যোগ করতে দেয়।

টাইপক্লাসের উদাহরণ হিসাবে কোনও কিছুর সংজ্ঞা দেওয়া যেতে পারে এবং ওপি থেকে ভিন্ন আপনি আপনার কীগুলি বিশেষত যোগ করতে চান না। অথবা, আপনি কোনও সংযোগের পরিবর্তে কোনও ইউনিয়নে পরিচালনা করতে চাইতে পারেন। স্কালাজ Mapএই উদ্দেশ্যে অতিরিক্ত ফাংশন যুক্ত করে :

https://oss.sonatype.org/service/local/repositories/snapshots/archive/org/scalaz/scalaz_2.11/7.3.0-SNAPSHOT/scalaz_2.11-7.3.0-SNAPSHOT-javadoc.jar/!/ index.html # scalaz.std.MapFunctions

আপনি করতে পারেন

import scalaz.Scalaz._

map1 |+| map2 // As per other answers
map1.intersectWith(map2)(_ + _) // Do things other than sum the values

2

দ্রুত এবং সহজতম উপায়:

val m1 = Map(1 -> 1.0, 3 -> 3.0, 5 -> 5.2)
val m2 = Map(0 -> 10.0, 3 -> 3.0)
val merged = (m2 foldLeft m1) (
  (acc, v) => acc + (v._1 -> (v._2 + acc.getOrElse(v._1, 0.0)))
)

এইভাবে, প্রতিটি উপাদান অবিলম্বে মানচিত্রে যুক্ত হয়েছে।

দ্বিতীয় ++উপায়টি হ'ল:

map1 ++ map2.map { case (k,v) => k -> (v + map1.getOrElse(k,0)) }

প্রথম উপায়ের বিপরীতে, দ্বিতীয় মানচিত্রে প্রতিটি উপাদানের জন্য দ্বিতীয় উপায়ে একটি নতুন তালিকা তৈরি করা হবে এবং পূর্ববর্তী মানচিত্রে সংক্ষিপ্ত করে রাখা হবে।

caseঅভিব্যক্তি পরোক্ষভাবে একটি নতুন ব্যবহার তালিকা তৈরি করে unapplyপদ্ধতি।


1

এটাই আমি নিয়ে এসেছি ...

def mergeMap(m1: Map[Char, Int],  m2: Map[Char, Int]): Map[Char, Int] = {
   var map : Map[Char, Int] = Map[Char, Int]() ++ m1
   for(p <- m2) {
      map = map + (p._1 -> (p._2 + map.getOrElse(p._1,0)))
   }
   map
}

1

টাইপক্লাস প্যাটার্ন ব্যবহার করে আমরা যে কোনও সংখ্যাসূচক প্রকারটি মার্জ করতে পারি:

object MapSyntax {
  implicit class MapOps[A, B](a: Map[A, B]) {
    def plus(b: Map[A, B])(implicit num: Numeric[B]): Map[A, B] = {
      b ++ a.map { case (key, value) => key -> num.plus(value, b.getOrElse(key, num.zero)) }
    }
  }
}

ব্যবহার:

import MapSyntax.MapOps

map1 plus map2

মানচিত্রের ক্রম মার্জ করা:

maps.reduce(_ plus _)

0

আমি কাজটি করার জন্য একটি ছোট ফাংশন পেয়েছি, এটি আমার ছোট লাইব্রেরিতে কিছু ঘন ঘন ব্যবহৃত কার্যকারিতার জন্য যা স্ট্যান্ডার্ড লিব না। এটি কেবলমাত্র হ্যাশম্যাপস নয়, সব ধরণের মানচিত্রের জন্য কাজ করা উচিত

এখানে ব্যবহার

scala> import com.daodecode.scalax.collection.extensions._
scala> val merged = Map("1" -> 1, "2" -> 2).mergedWith(Map("1" -> 1, "2" -> 2))(_ + _)
merged: scala.collection.immutable.Map[String,Int] = Map(1 -> 2, 2 -> 4)

https://github.com/jozic/scalax-collection/blob/master/README.md#mergedwith

এবং এখানে শরীর

def mergedWith(another: Map[K, V])(f: (V, V) => V): Repr =
  if (another.isEmpty) mapLike.asInstanceOf[Repr]
  else {
    val mapBuilder = new mutable.MapBuilder[K, V, Repr](mapLike.asInstanceOf[Repr])
    another.foreach { case (k, v) =>
      mapLike.get(k) match {
        case Some(ev) => mapBuilder += k -> f(ev, v)
        case _ => mapBuilder += k -> v
      }
    }
    mapBuilder.result()
  }

https://github.com/jozic/scalax-collection/blob/master/src%2Fmain%2Fscala%2Fcom%2Fdaodecode%2Fscalax%2Fcollection%2Fextensions%2Fpackage.scala#L190

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