2009-08-11 15 views
28

के संग्रह को मर्ज कैसे करें मेरे पास मानचित्र [स्ट्रिंग, डबल] की एक सूची है, और मैं अपनी सामग्री को एक मानचित्र [स्ट्रिंग, डबल] में विलय करना चाहता हूं। मैं इसे एक बेवकूफ तरीके से कैसे करना चाहिए? मुझे कल्पना है कि मुझे इसे एक गुना के साथ करने में सक्षम होना चाहिए। कुछ की तरह:स्कैला: मैप्स

val newMap = Map[String, Double]() /: listOfMaps { (accumulator, m) => ... } 

इसके अलावा, मैं एक सामान्य तरीके से कुंजी टकराव को संभालने के लिए चाहते हैं। यही है, अगर मैं पहले से मौजूद मानचित्र में एक कुंजी जोड़ता हूं, तो मुझे एक ऐसा फ़ंक्शन निर्दिष्ट करने में सक्षम होना चाहिए जो एक डबल (इस मामले में) देता है और उस कुंजी के लिए मौजूदा मान लेता है, साथ ही जिस मूल्य को मैं जोड़ने की कोशिश कर रहा हूं । यदि कुंजी अभी तक मानचित्र में मौजूद नहीं है, तो बस इसे और उसके मान को अनलर्टेड करें।

मेरे विशिष्ट मामले में मैं एक नक्शा [स्ट्रिंग, डबल] बनाना चाहता हूं जैसे कि मानचित्र में पहले से ही एक कुंजी है, तो डबल को मौजूदा मानचित्र मान में जोड़ा जाएगा।

मैं अपने विशिष्ट कोड में परिवर्तनीय मानचित्रों के साथ काम कर रहा हूं, लेकिन यदि संभव हो तो मुझे अधिक सामान्य समाधानों में दिलचस्पी है।

उत्तर

23

इस एक के बारे में कैसे:

def mergeMap[A, B](ms: List[Map[A, B]])(f: (B, B) => B): Map[A, B] = 
    (Map[A, B]() /: (for (m <- ms; kv <- m) yield kv)) { (a, kv) => 
    a + (if (a.contains(kv._1)) kv._1 -> f(a(kv._1), kv._2) else kv) 
    } 

val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4)) 
val mm = mergeMap(ms)((v1, v2) => v1 + v2) 

println(mm) // prints Map(hello -> 5.5, world -> 2.2, goodbye -> 3.3) 

और यह 2.7.5 और 2.8.0 दोनों में काम करता है।

+0

यह ठीक है कि मैं इसे शुरू में कैसे करने की कोशिश कर रहा था। मैंने वहां समझने के लिए नहीं सोचा था - मैं अभी भी इस तरह उनका उपयोग करने के लिए उपयोग कर रहा हूं, लेकिन यह समझ में आता है। इस मामले में मैं देख सकता हूं कि यह पाइथन की सूची समझों की तरह कितना है, जिसे मैं अधिक आरामदायक हूं। अगर कॉल के अंदर अभिव्यक्ति को। +() के परिणामस्वरूप परिणाम-असर के उपयोग की तरह भी। – Jeff

+0

साफ जवाब। प्रशंसा –

37

ठीक है, तुम कर सकते हो:

mapList reduce (_ ++ _) 

टक्कर के लिए विशेष आवश्यकता के लिए छोड़कर।

के बाद आपको लगता है कि विशेष आवश्यकता है, शायद सबसे अच्छा कुछ इस तरह (2.8) कर होगा:

def combine(m1: Map, m2: Map): Map = { 
    val k1 = Set(m1.keysIterator.toList: _*) 
    val k2 = Set(m2.keysIterator.toList: _*) 
    val intersection = k1 & k2 

    val r1 = for(key <- intersection) yield (key -> (m1(key) + m2(key))) 
    val r2 = m1.filterKeys(!intersection.contains(_)) ++ m2.filterKeys(!intersection.contains(_)) 
    r2 ++ r1 
} 

फिर आप इस विधि का नक्शा वर्ग के लिए दलाल मेरी लाइब्रेरी पैटर्न के माध्यम से जोड़ सकते हैं, और के बजाय "++" मूल उदाहरण में इसका इस्तेमाल करते हैं:

class CombiningMap(m1: Map[Symbol, Double]) { 
    def combine(m2: Map[Symbol, Double]) = { 
    val k1 = Set(m1.keysIterator.toList: _*) 
    val k2 = Set(m2.keysIterator.toList: _*) 
    val intersection = k1 & k2 
    val r1 = for(key <- intersection) yield (key -> (m1(key) + m2(key))) 
    val r2 = m1.filterKeys(!intersection.contains(_)) ++ m2.filterKeys(!intersection.contains(_)) 
    r2 ++ r1 
    } 
} 

// Then use this: 
implicit def toCombining(m: Map[Symbol, Double]) = new CombiningMap(m) 

// And finish with: 
mapList reduce (_ combine _) 

इस 2.8 में लिखा गया था, वहीं तो keysIterator हो जाता है keys 2.7 के लिए, filterKeys 01,239,390 के संदर्भ में लिखा जा करना पड़ सकता हैऔर map, &** बन जाता है, और इसी तरह, यह बहुत अलग नहीं होना चाहिए।

+1

किंडा कि आवश्यकता को अनदेखा करने की बात को हरा दिया। – Jeff

+0

यही कारण है कि मैंने इसका विस्तार किया। –

+0

आधुनिक स्काला के साथ: वैल k1 = m1.keysIterator.toSet – qerub

2

दिलचस्प है, यह एक बिट के साथ चारों ओर noodling, मुझे मिल गया (2.7.5) पर निम्नलिखित:

जनरल मानचित्र:

def mergeMaps[A,B](collisionFunc: (B,B) => B)(listOfMaps: Seq[scala.collection.Map[A,B]]): Map[A, B] = { 
    listOfMaps.foldLeft(Map[A, B]()) { (m, s) => 
     Map(
     s.projection.map { pair => 
     if (m contains pair._1) 
      (pair._1, collisionFunc(m(pair._1), pair._2)) 
     else 
      pair 
     }.force.toList:_*) 
    } 
    } 

लेकिन यार, कि प्रक्षेपण और मजबूर कर के साथ घृणित है और tolist और क्या नहीं। अलग सवाल: गुना के भीतर उससे निपटने का एक बेहतर तरीका क्या है?

परिवर्तनशील मैप्स, जो है क्या मैं एक कम सामान्य समाधान के साथ अपने कोड में साथ काम कर रहा था, और के लिए, मैं यह मिल गया:

def mergeMaps[A,B](collisionFunc: (B,B) => B)(listOfMaps: List[mutable.Map[A,B]]): mutable.Map[A, B] = { 
    listOfMaps.foldLeft(mutable.Map[A,B]()) { 
     (m, s) => 
     for (k <- s.keys) { 
     if (m contains k) 
      m(k) = collisionFunc(m(k), s(k)) 
     else 
      m(k) = s(k) 
     } 
     m 
    } 
    } 

एक छोटा सा क्लीनर लगता है कि है, लेकिन केवल अस्थायी के साथ काम करेंगे मानचित्र के रूप में लिखा है। दिलचस्प बात यह है कि मैंने पहली बार उपरोक्त कोशिश की थी (इससे पहले कि मैंने सवाल पूछा)/फ़ोल्ड लेफ्ट की बजाय, लेकिन मुझे टाइप त्रुटियां मिल रही थीं। मैंने सोचा /: और foldLeft मूल रूप से समकक्ष थे, लेकिन संकलक शिकायत करते रहे कि मुझे (एम, एस) के लिए स्पष्ट प्रकार की आवश्यकता है। उसके साथ क्या है?

+0

आपको यहां 'बल' का उपयोग करने की आवश्यकता नहीं है, क्योंकि 'toList' सख्त है। –

+0

'foldLeft' बनाम' /: 'के लिए, आप ऑब्जेक्ट को महसूस करते हैं और पहला तर्क उनके बीच बदल जाता है? अभिव्यक्ति 'x foldLeft y' 'y /: x' के बराबर है। इसके अलावा, वाक्यविन्यास के मुद्दों का एक गुच्छा है। , (तह अभिव्यक्ति) '' जबकि foldLeft' के रूप में 'x.foldLeft (y) (अभिव्यक्ति तह)' इस्तेमाल किया जा सकता: मूल रूप से, आप * 'लिखने के लिए (एक्स y /) * है। –

+0

हां, मुझे समाप्त होने वाली विधियों के बारे में पता था: ऑब्जेक्ट को तर्क के साथ स्वैप करना। इस तरह मैंने इस सवाल में उदाहरण लिखा था। मैं y /: x को parens में रखना भूल गया था, और मैं शर्त लगाता हूं कि यह एक समस्या थी। धन्यवाद! – Jeff

3

मैं जल्दी से इस सवाल को पढ़ने तो मुझे यकीन है कि अगर मैं कुछ याद कर रहा हूँ (जैसे कि यह 2.7.x या कोई scalaz के लिए काम करने के लिए है) नहीं कर रहा हूँ:

import scalaz._ 
import Scalaz._ 
val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4)) 
ms.reduceLeft(_ |+| _) 
// returns Map(goodbye -> 3.3, hello -> 5.5, world -> 2.2) 

आप के लिए monoid परिभाषा को बदल सकते हैं डबल और मूल्यों जमा करने के लिए, यहाँ अधिकतम हो रही एक और तरीका मिल:

implicit val dbsg: Semigroup[Double] = semigroup((a,b) => math.max(a,b)) 
ms.reduceLeft(_ |+| _) 
// returns Map(goodbye -> 3.3, hello -> 4.4, world -> 2.2) 
+0

+1, हालांकि मैं 'ms.suml' है, जो अधिक संक्षिप्त है और एक खाली सूची पर एक क्रम अपवाद फेंक नहीं का अतिरिक्त लाभ है लिखना चाहते हैं। –

+0

@TravisBrown, हां, स्केलज़ में इतने सारे सुविधाजनक कार्यों; हालांकि 'suml' केवल scalaz 7 हो सकता है? मैं केवल 6.x में 'sumr' देखता हूं। – huynhjl

0

एक oneliner सहायक-समारोह, जिसका उपयोग scalaz का उपयोग कर के रूप में लगभग के रूप में साफ लिखा है:

def mergeMaps[K,V](m1: Map[K,V], m2: Map[K,V])(f: (V,V) => V): Map[K,V] = 
    (m1 -- m2.keySet) ++ (m2 -- m1.keySet) ++ (for (k <- m1.keySet & m2.keySet) yield { k -> f(m1(k), m2(k)) }) 

val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4)) 
ms.reduceLeft(mergeMaps(_,_)(_ + _)) 
// returns Map(goodbye -> 3.3, hello -> 5.5, world -> 2.2) 

के लिए परम पठनीयता एक अंतर्निहित कस्टम प्रकार में लपेट:

class MyMap[K,V](m1: Map[K,V]) { 
    def merge(m2: Map[K,V])(f: (V,V) => V) = 
    (m1 -- m2.keySet) ++ (m2 -- m1.keySet) ++ (for (k <- m1.keySet & m2.keySet) yield { k -> f(m1(k), m2(k)) }) 
} 
implicit def toMyMap[K,V](m: Map[K,V]) = new MyMap(m) 

val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4)) 
ms reduceLeft { _.merge(_)(_ + _) } 
2

मैं इस बारे में एक ब्लॉग पोस्ट में लिखा है, यह बाहर की जाँच:

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

मूल रूप से का उपयोग कर scalaz अर्द्ध समूह आप प्राप्त कर सकते हैं यह बहुत आसानी से

कुछ ऐसा दिखाई देगा:

import scalaz.Scalaz._ 
    listOfMaps reduce(_ |+| _) 
+0

आप वास्तव में 'listOfMaps.suml' का उपयोग कर सकते हैं; यह वही काम करना चाहिए। जो मैं समझता हूं उससे इसका अर्थ है SumLeft, जहां यह अनिवार्य रूप से 'lowLeft (_ | + | _) ' – JBarber

17

मैं हैरान हूँ कि कोई भी अभी तक इस समाधान के साथ आते हैं:

  1. खर-पतवार बाहर किसी भी डुप्लिकेट एक भी नक्शा करने के लिए सूची में मर्ज करता:

    myListOfMaps.flatten.toMap 
    

    आपको क्या चाहिए क्या वास्तव में कुंजी

उदाहरण:

scala> List(Map('a -> 1), Map('b -> 2), Map('c -> 3), Map('a -> 4, 'b -> 5)).flatten.toMap 
res7: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 4, 'b -> 5, 'c -> 3) 

flatten tuples के एक फ्लैट सूची में नक्शे की सूची बदल जाता है, toMap सभी डुप्लिकेट कुंजी के साथ एक नक्शे में tuples की सूची बदल जाता है हटा दिया

+2

चलाता है यह वही है जो मुझे चाहिए, लेकिन डुप्लिकेट कुंजी के लिए मानों को योग नहीं करता है क्योंकि ओपी की आवश्यकता होती है। –

+0

या आप flatMap का उपयोग कर सकते हैं – wbmrcb

संबंधित मुद्दे