ग्रूवी में सूची से नक्शा बनाने के लिए शॉर्टकट?


106

मैं इसके लिए कुछ उपाय करना चाहूंगा:

Map rowToMap(row) {
    def rowMap = [:];
    row.columns.each{ rowMap[it.name] = it.val }
    return rowMap;
}

GDK सामान जिस तरह से दिया गया है, मुझे उम्मीद है कि मैं कुछ ऐसा कर पाऊंगा:

Map rowToMap(row) {
    row.columns.collectMap{ [it.name,it.val] }
}

लेकिन मैंने डॉक्स में कुछ भी नहीं देखा है ... क्या मुझे कुछ याद आ रहा है? या मैं अभी भी बहुत आलसी हूँ?


2
आमिर की टिप्पणी अब सही उत्तर है: stackoverflow.com/a/4484958/27561
रॉबर्ट फिशर

जवाबों:


119

मैं हाल ही में वास्तव में ऐसा करने की आवश्यकता में आया हूं: एक सूची को मानचित्र में परिवर्तित करना। यह सवाल Groovy संस्करण 1.7.9 से बाहर आने से पहले पोस्ट किया गया था, इसलिए यह विधि collectEntriesअभी तक मौजूद नहीं थी। यह उस collectMapविधि के समान काम करता है जो प्रस्तावित थी :

Map rowToMap(row) {
    row.columns.collectEntries{[it.name, it.val]}
}

यदि किसी कारण से आप पुराने ग्रूवी संस्करण के साथ फंस गए हैं, तो injectविधि का उपयोग भी किया जा सकता है (जैसा कि यहां प्रस्तावित है )। यह थोड़ा संशोधित संस्करण है जो केवल एक अभिव्यक्ति को बंद करने के अंदर लेता है (सिर्फ चरित्र की बचत के लिए!)।

Map rowToMap(row) {
    row.columns.inject([:]) {map, col -> map << [(col.name): col.val]}
}

की जगह +ऑपरेटर का भी इस्तेमाल किया जा सकता है <<


28

"इंजेक्ट" देखें। वास्तविक कार्यात्मक प्रोग्रामिंग विक्स इसे "गुना" कहते हैं।

columns.inject([:]) { memo, entry ->
    memo[entry.name] = entry.val
    return memo
}

और, जब आप इस पर होते हैं, तो आप शायद मेटाक्लास पर दाईं ओर के बजाय श्रेणियाँ के रूप में तरीकों को परिभाषित करना चाहते हैं। इस तरह, आप इसे सभी संग्रह के लिए एक बार परिभाषित कर सकते हैं:

class PropertyMapCategory {
    static Map mapProperty(Collection c, String keyParam, String valParam) {
        return c.inject([:]) { memo, entry ->
            memo[entry[keyParam]] = entry[valParam]
            return memo
        }
    }
}

उदाहरण का उपयोग:

use(PropertyMapCategory) {
    println columns.mapProperty('name', 'val')
}

मुझे लगता है कि यह ग्रूवी में इंजेक्शन का नाम है क्योंकि यह इंजेक्शन से प्रेरित हो सकता है: में: स्मॉलटॉक में: | सूची योग | सूची: = आदेशित नया जोड़ना: 1; जोड़ें: 2; जोड़ें: 3; स्वयं। sum: = सूची इंजेक्षन: 0 में: [: a: b | ए + बी]। ट्रांसक्रिप्ट cr; शो: राशि। "प्रिंट 6"
OlliP

13

क्या यह प्रश्न पूछे जाने पर GroupBy विधि उपलब्ध नहीं थी?


लगता है जैसे - यह 1.8.1, 2011 से है। यह सवाल 2008 में पूछा गया था। लेकिन किसी भी मामले में, GroupBy अब वास्तव में जाने का रास्ता है।
mvmn

जैसा कि आप GroupBy के दस्तावेज़ में देख सकते हैं, यह मूल रूप से समूहों में तत्वों को समूहित करता है, जहाँ प्रत्येक समूह में एक निश्चित कुंजी से मेल खाते तत्व होते हैं। इसलिए, इसका रिटर्न प्रकार Map<K, List<V>>यह है कि ओपी रिटर्न प्रकार के साथ एक विधि की तलाश कर रहा है Map<K, V>, इसलिए इस मामले में GroupBy काम नहीं करता है।
क्रिज़िसे प्रेज़ीगुडकी

6

यदि आपको जिस चीज की आवश्यकता है वह एक साधारण कुंजी-मूल्य जोड़ी है, तो विधि collectEntriesको पर्याप्त होना चाहिए। उदाहरण के लिए

def names = ['Foo', 'Bar']
def firstAlphabetVsName = names.collectEntries {[it.charAt(0), it]} // [F:Foo, B:Bar]

लेकिन अगर आप मल्टीमैप जैसी संरचना चाहते हैं, जिसमें प्रति कुंजी कई मान हों, तो आप groupByविधि का उपयोग करना चाहेंगे

def names = ['Foo', 'Bar', 'Fooey']
def firstAlphabetVsNames = names.groupBy { it.charAt(0) } // [F:[Foo, Fooey], B:[Bar]]


5

ठीक है ... मैंने इसके साथ थोड़ा और खेला है और मुझे लगता है कि यह एक बहुत अच्छा तरीका है ...

def collectMap = {Closure callback->
    def map = [:]
    delegate.each {
        def r = callback.call(it)
        map[r[0]] = r[1]
    }
    return map
}
ExpandoMetaClass.enableGlobally()
Collection.metaClass.collectMap = collectMap
Map.metaClass.collectMap = collectMap

अब मानचित्र या संग्रह के किसी भी उपवर्ग में यह विधि है ...

यहाँ मैं एक मानचित्र में कुंजी / मान को उल्टा करने के लिए इसका उपयोग करता हूं

[1:2, 3:4].collectMap{[it.value, it.key]} == [2:1, 4:3]

और यहाँ मैं एक सूची से एक नक्शा बनाने के लिए इसका उपयोग करता हूं

[1,2].collectMap{[it,it]} == [1:1, 2:2]

अब मैं इसे केवल एक वर्ग में पॉप करता हूं जिसे मेरा ऐप कहा जाता है और यह विधि मेरे कोड में उपलब्ध है।

संपादित करें:

सभी सरणियों में विधि जोड़ने के लिए ...

Object[].metaClass.collectMap = collectMap

1

मुझे इसमें निर्मित कुछ भी नहीं मिल रहा है ... लेकिन ExpandoMetaClass का उपयोग करके मैं यह कर सकता हूं:

ArrayList.metaClass.collectMap = {Closure callback->
    def map = [:]
    delegate.each {
        def r = callback.call(it)
        map[r[0]] = r[1]
    }
    return map
}

यह सभी ArrayLists के लिए संग्रहण विधि को जोड़ता है ... मुझे यकीन नहीं है कि इसे सूची या संग्रह में शामिल करने से काम क्यों नहीं हुआ .. मुझे लगता है कि यह एक और प्रश्न के लिए है ... लेकिन अब मैं यह कर सकता हूं ...

assert ["foo":"oof", "42":"24", "bar":"rab"] ==
            ["foo", "42", "bar"].collectMap { return [it, it.reverse()] }

एक बंद के साथ गणना की गई सूची की सूची से ... बिल्कुल वही जो मैं देख रहा था।

संपादित करें: कारण कि मैं इंटरफेस की सूची और संग्रह में विधि नहीं जोड़ सका क्योंकि मैंने ऐसा नहीं किया था:

List.metaClass.enableGlobally()

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

(0..2).collectMap{[it, it*2]}

जो नक्शा देता है: [0: 0, 1: 2, 2: 4]


0

इस तरह के किसी चीज़ के बारे में क्या?

// setup
class Pair { 
    String k; 
    String v; 
    public Pair(def k, def v) { this.k = k ; this.v = v; }
}
def list = [ new Pair('a', 'b'), new Pair('c', 'd') ]

// the idea
def map = [:]
list.each{ it -> map.putAt(it.k, it.v) }

// verify
println map['c']

यह मूल रूप से मेरे प्रश्न के समान ही है ... मेरे पास बस नक्शा था [it.k] =.v के बजाय .putAt मैं एक लाइनर की तलाश में था।
danb
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.