स्विफ्ट में एक शब्दकोश में नक्शा () लागू करने का सबसे साफ तरीका क्या है?


154

मैं शब्दकोश में सभी कुंजियों पर एक फ़ंक्शन मैप करना चाहता हूं। मैं उम्मीद कर रहा था कि निम्नलिखित कुछ काम करेगा, लेकिन फ़िल्टर सीधे शब्दकोश में लागू नहीं किया जा सकता है। इसे प्राप्त करने का सबसे साफ तरीका क्या है?

इस उदाहरण में, मैं प्रत्येक मूल्य को 1 से बढ़ाने की कोशिश कर रहा हूं। हालांकि यह उदाहरण के लिए आकस्मिक है - मुख्य उद्देश्य यह है कि एक शब्दकोश में नक्शा () कैसे लागू किया जाए।

var d = ["foo" : 1, "bar" : 2]

d.map() {
    $0.1 += 1
}

1
शब्दकोश में नक्शा कार्य नहीं है, इसलिए यह स्पष्ट नहीं है कि आप क्या करने की कोशिश कर रहे हैं।
डेविड बेरी

7
हां - मुझे पता है कि डिक्शनरी में मैप फंक्शन नहीं है। इस सवाल को फिर से परिभाषित किया जा सकता है कि पूरे शब्दकोश में पुनरावृति की आवश्यकता के बिना इसे कैसे पूरा किया जा सकता है।
मारिया ज्वेरेना

2
आपको अभी भी पूरे शब्दकोश पर पुनरावृति करने की आवश्यकता है क्योंकि यह क्या mapकरता है। जो आप पूछ रहे हैं वह विस्तार / बंद करने के पीछे पुनरावृत्ति को छिपाने का एक तरीका है ताकि आपको हर बार इसका उपयोग करने के बाद इसे न देखना पड़े।
जोसेफ मार्क

1
स्विफ्ट 4 के लिए, मेरा उत्तर देखें जो आपकी समस्या को हल करने के 5 अलग-अलग तरीकों को दिखाता है।
इमानौ पेटिट

जवाबों:


281

स्विफ्ट 4+

खुशखबरी! स्विफ्ट 4 में एक mapValues(_:)विधि शामिल है जो एक ही कुंजियों के साथ एक शब्दकोश की एक प्रति का निर्माण करती है, लेकिन विभिन्न मूल्य। इसमें एक filter(_:)अधिभार भी शामिल है जो ट्यूपल्स के एक मनमाने अनुक्रम से बनाने के लिए एक Dictionary, init(uniqueKeysWithValues:)और init(_:uniquingKeysWith:)इनिशियलाइज़र लौटाता है Dictionary। इसका मतलब है कि, यदि आप कुंजियों और मूल्यों दोनों को बदलना चाहते हैं, तो आप कुछ ऐसा कह सकते हैं:

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })

साथ में शब्दकोशों को मर्ज करने के लिए नए एपीआई भी हैं, लापता तत्वों के लिए एक डिफ़ॉल्ट मान को प्रतिस्थापित करना, मूल्यों को समूहीकृत करना (किसी फ़ंक्शन में संग्रह को मैप करना, किसी फ़ंक्शन पर संग्रह को मैप करने के परिणाम द्वारा कुंजीबद्ध), और बहुत कुछ।

प्रस्ताव की चर्चा के दौरान, एसई -0165 , जिसने इन विशेषताओं को पेश किया, मैंने कई बार इस स्टैक ओवरफ्लो जवाब को लाया, और मुझे लगता है कि अपवाह की सरासर संख्या ने मांग को प्रदर्शित करने में मदद की। तो स्विफ्ट को बेहतर बनाने में आपकी मदद के लिए धन्यवाद!


6
यह निश्चित रूप से अपूर्ण है, लेकिन हमें पूर्ण को अच्छे का दुश्मन नहीं बनने देना चाहिए। एक mapजो परस्पर विरोधी कुंजियों का उत्पादन कर सकता है वह अभी भी mapकई स्थितियों में उपयोगी है। (दूसरी ओर, आप तर्क दे सकते हैं कि केवल मानों की मैपिंग mapशब्दकोशों पर एक प्राकृतिक व्याख्या है - दोनों मूल पोस्टर के उदाहरण और मेरा केवल मूल्यों को संशोधित करते हैं, और अवधारणात्मक रूप से, mapएक सरणी पर केवल मूल्यों को संशोधित करता है, न कि सूचकांकों को।)
ब्रेंट रॉयल-गॉर्डन

2
आपका आखिरी कोड ब्लॉक याद आ रहा है try:return Dictionary<Key, OutValue>(try map { (k, v) in (k, try transform(v)) })
jpsim

9
स्विफ्ट 2.1 के लिए अपडेट किया गया? हो रही हैDictionaryExtension.swift:13:16: Argument labels '(_:)' do not match any available overloads
प्रति एरिकसन

4
स्विफ्ट 2.2 के साथ मुझे Argument labels '(_:)' do not match any available overloadsवह आखिरी एक्सटेंशन लागू करते समय मिल रहा है । वास्तव में यह सुनिश्चित करें कि यह कैसे हल करने के लिए: /
Erken

2
@Audio यह कोड स्निपेट Dictionaryपिछले उदाहरणों में से एक में जोड़े गए इनिलाइज़र पर निर्भर करता है । सुनिश्चित करें कि आप दोनों को शामिल करते हैं।
ब्रेंट रॉयल-गॉर्डन

65

स्विफ्ट 5 के साथ, आप अपनी समस्या को हल करने के लिए निम्नलिखित पांच स्निपेट में से एक का उपयोग कर सकते हैं ।


# 1। Dictionary mapValues(_:)विधि का उपयोग करना

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

let newDictionary = dictionary.mapValues { value in
    return value + 1
}
//let newDictionary = dictionary.mapValues { $0 + 1 } // also works

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

# 2। Dictionary mapविधि और init(uniqueKeysWithValues:)इनिशलाइज़र का उपयोग करना

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

let tupleArray = dictionary.map { (key: String, value: Int) in
    return (key, value + 1)
}
//let tupleArray = dictionary.map { ($0, $1 + 1) } // also works

let newDictionary = Dictionary(uniqueKeysWithValues: tupleArray)

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

# 3। Dictionary reduce(_:_:)विधि या reduce(into:_:)विधि का उपयोग करना

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

let newDictionary = dictionary.reduce([:]) { (partialResult: [String: Int], tuple: (key: String, value: Int)) in
    var result = partialResult
    result[tuple.key] = tuple.value + 1
    return result
}

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
let dictionary = ["foo": 1, "bar": 2, "baz": 5]

let newDictionary = dictionary.reduce(into: [:]) { (result: inout [String: Int], tuple: (key: String, value: Int)) in
    result[tuple.key] = tuple.value + 1
}

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

# 4। Dictionary subscript(_:default:)सबस्क्रिप्ट का उपयोग करना

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

var newDictionary = [String: Int]()
for (key, value) in dictionary {
    newDictionary[key, default: value] += 1
}

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

# 5। Dictionary subscript(_:)सबस्क्रिप्ट का उपयोग करना

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

var newDictionary = [String: Int]()
for (key, value) in dictionary {
    newDictionary[key] = value + 1
}

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

कृपया उदाहरण दें कि कुंजियाँ मानों को नहीं बदलतीं। यह मददगार धन्यवाद।
योगेश पटेल

18

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

यहां एक एक्सटेंशन है, mapValuesजो आपको सिर्फ मूल्यों को मैप करने की अनुमति देता है। ध्यान दें कि यह initकुंजी / मान युग्मों के अनुक्रम से भी शब्दकोष का विस्तार करता है, जो इसे एक सरणी से आरंभ करने की तुलना में थोड़ा अधिक सामान्य है:

extension Dictionary {
    init<S: SequenceType where S.Generator.Element == Element>
      (_ seq: S) {
        self.init()
        for (k,v) in seq {
            self[k] = v
        }
    }

    func mapValues<T>(transform: Value->T) -> Dictionary<Key,T> {
        return Dictionary<Key,T>(zip(self.keys, self.values.map(transform)))
    }

}

..इसका उपयोग कैसे किया जा सकता है? Im नया करने के लिए तेजी से दिमाग यहाँ एक उदाहरण डाल?
फ्लोयू। SimpleUITesting.com

जब मैंने myDictionary.map की कोशिश की, क्लोजर के अंदर, मुझे नियमित रूप से एक और नक्शा करना पड़ा। यह काम करता है, लेकिन क्या इसके इस्तेमाल का तरीका है?
फ्लोयू। SimpleUITesting.com

क्या कहीं गारंटी है कि कुंजी और मूल्यों के क्रम को अलग-अलग कहा जाता है, और इस प्रकार ज़िप के लिए उपयुक्त है?
एरोन ज़िनमैन

जब आप सिर्फ उपयोग कर सकते हैं तो एक नया init क्यों बनाएं func mapValues<T>(_ transform: (_ value: Value) -> T) -> [Key: T] { var result = [Key: T]() for (key, val) in self { result[key] = transform(val) } return result }। मुझे लगता है कि यह बेहतर भी पढ़ता है। क्या कोई प्रदर्शन मुद्दा है?
मार्सेल

12

सबसे साफ तरीका सिर्फ mapशब्दकोश में जोड़ना है:

extension Dictionary {
    mutating func map(transform: (key:KeyType, value:ValueType) -> (newValue:ValueType)) {
        for key in self.keys {
            var newValue = transform(key: key, value: self[key]!)
            self.updateValue(newValue, forKey: key)
        }
    }
}

जाँच रहा है कि यह काम करता है:

var dic = ["a": 50, "b": 60, "c": 70]

dic.map { $0.1 + 1 }

println(dic)

dic.map { (key, value) in
    if key == "a" {
        return value
    } else {
        return value * 2
    }
}

println(dic)

आउटपुट:

[c: 71, a: 51, b: 61]
[c: 142, a: 51, b: 122]

1
इस समस्या को मैं इस दृष्टिकोण के साथ देख रहा हूं जो कि SequenceType.mapएक परिवर्तनशील कार्य नहीं है। आपके उदाहरण के लिए मौजूदा अनुबंध का पालन करने के लिए फिर से लिखा जा सकता है map, लेकिन यह स्वीकृत उत्तर के समान है।
क्रिस्टोफर पिक्सेले

7

आप reduceनक्शे के बजाय भी उपयोग कर सकते हैं । reduceकुछ mapभी करने और अधिक करने में सक्षम है !

let oldDict = ["old1": 1, "old2":2]

let newDict = reduce(oldDict, [String:Int]()) { dict, pair in
    var d = dict
    d["new\(pair.1)"] = pair.1
    return d
}

println(newDict)   //  ["new1": 1, "new2": 2]

इसे विस्तार से लपेटना काफी आसान होगा, लेकिन विस्तार के बिना भी यह आपको वही करने देता है जो आप एक फ़ंक्शन कॉल के साथ चाहते हैं।


10
इस दृष्टिकोण का नकारात्मक पक्ष यह है कि शब्दकोश की एक नई प्रति हर बार जब आप एक नई कुंजी जोड़ते हैं, तो यह फ़ंक्शन बहुत ही अयोग्य है (आशावादी आपको बचा सकता है )।
एयरस्पीड वेलोसिटी

यह एक अच्छी बात है ... एक बात जो मुझे अभी तक अच्छी तरह से समझ में नहीं आई है, वह है स्विफ्ट के मेमोरी मैनेजमेंट के इंटर्नल्स। मैं इस तरह की टिप्पणियों की सराहना करता हूं जो मुझे इसके बारे में सोचते हैं :)
हारून रासमुसेन

1
अस्थायी संस्करण की आवश्यकता नहीं है और अब स्विफ्ट 2.0 में एक विधि है:let newDict = oldDict.reduce([String:Int]()) { (var dict, pair) in dict["new\(pair.1)"] = pair.1; return dict }
ज़मी जूल

4

यह पता चला है कि आप ऐसा कर सकते हैं। आपको जो करना है MapCollectionView<Dictionary<KeyType, ValueType>, KeyType>, वह शब्दकोश keysविधि से लौटे से एक सरणी बना सकता है । ( जानकारी यहां ) आप इस सरणी को मैप कर सकते हैं, और अपडेट किए गए मानों को वापस शब्दकोश में पास कर सकते हैं।

var dictionary = ["foo" : 1, "bar" : 2]

Array(dictionary.keys).map() {
    dictionary.updateValue(dictionary[$0]! + 1, forKey: $0)
}

dictionary

क्या आप कृपया व्याख्या को जोड़ सकते हैं कि कैसे ["फू": 1, "बार": 2] से [("फू", 1), ("बार", 2)]
मारिया ज्वेरेना

@MariaZverina मुझे यकीन नहीं है कि आपका क्या मतलब है।
मिक मैक्लम

यदि आप ऊपर रूपांतरण कर सकते हैं, तो फ़िल्टर सीधे परिणामस्वरूप सरणी पर लागू किया जा सकता है
मारिया ज़वेरीना

@MariaZverina Gotcha हाँ, दुर्भाग्य से मुझे ऐसा करने का कोई तरीका नहीं पता है।
मिक मैक्लम

3

स्विफ्ट स्टैंडर्ड लाइब्रेरी रेफरेंस के अनुसार, नक्शा सरणियों का एक कार्य है। शब्दकोशों के लिए नहीं।

लेकिन आप कुंजियों को संशोधित करने के लिए अपने शब्दकोश को बदल सकते हैं:

var d = ["foo" : 1, "bar" : 2]

for (name, key) in d {
    d[name] = d[name]! + 1
}

3

मैं कस्टम ऑब्जेक्ट्स के साथ टाइप किए गए ऐरे में शब्दकोश को सही तरीके से मैप करने का एक तरीका ढूंढ रहा था। इस विस्तार में समाधान मिला:

extension Dictionary {
    func mapKeys<U> (transform: Key -> U) -> Array<U> {
        var results: Array<U> = []
        for k in self.keys {
            results.append(transform(k))
        }
        return results
    }

    func mapValues<U> (transform: Value -> U) -> Array<U> {
        var results: Array<U> = []
        for v in self.values {
            results.append(transform(v))
        }
        return results
    }

    func map<U> (transform: Value -> U) -> Array<U> {
        return self.mapValues(transform)
    }

    func map<U> (transform: (Key, Value) -> U) -> Array<U> {
        var results: Array<U> = []
        for k in self.keys {
            results.append(transform(k as Key, self[ k ]! as Value))
        }
        return results
    }

    func map<K: Hashable, V> (transform: (Key, Value) -> (K, V)) -> Dictionary<K, V> {
        var results: Dictionary<K, V> = [:]
        for k in self.keys {
            if let value = self[ k ] {
                let (u, w) = transform(k, value)
                results.updateValue(w, forKey: u)
            }
        }
        return results
    }
}

निम्नलिखित के रूप में इसका उपयोग करना:

self.values = values.map({ (key:String, value:NSNumber) -> VDLFilterValue in
    return VDLFilterValue(name: key, amount: value)
})

3

स्विफ्ट 3

मैं स्विफ्ट 3 में एक आसान तरीका आज़माता हूं।

मैं [String: String?] को [String: String] मैप करने के लिए कहना चाहता हूं, मैं मैप या फ्लैट मैप के बजाय forEach का उपयोग करता हूं।

    let oldDict = ["key0": "val0", "key1": nil, "key1": "val2","key2": nil]
    var newDict = [String: String]()
    oldDict.forEach { (source: (key: String, value: String?)) in
        if let value = source.value{
            newDict[source.key] = value
        }
    }

अवांछनीय के रूप में यह एक नया सरणी बनाता है और इसे करने के लिए अपील करता है
स्टीव कुओ

2

स्विफ्ट 5

शब्दकोश का मानचित्र कार्य इस वाक्य रचना के साथ आता है।

dictData.map(transform: ((key: String, value: String)) throws -> T)

आप बस इसके लिए क्लोजर वैल्यू सेट कर सकते हैं

var dictData: [String: String] = [
    "key_1": "test 1",
    "key_2": "test 2",
    "key_3": "test 3",
    "key_4": "test 4",
    "key_5": "test 5",
]

dictData.map { (key, value) in
    print("Key :: \(key), Value :: \(value)")
}

आउटपुट होगा ::

Key :: key_5, Value :: test 5
Key :: key_1, Value :: test 1
Key :: key_3, Value :: test 3
Key :: key_2, Value :: test 2
Key :: key_4, Value :: test 4

यह चेतावनी दे सकता है Result of call to 'map' is unused

फिर बस सेट _ = वेरिएबल से पहले करें।

हो सकता है यह आपको धन्यवाद करने में मदद करे


1

मुझे लगता है कि समस्या यह है कि हमारे मूल शब्दकोष की चाबी रखें और इसके सभी मूल्यों को किसी न किसी तरह से मानचित्रित करें।

आइए हम सामान्यीकरण करें और कहें कि हम सभी मूल्यों को दूसरे प्रकार से मैप करना चाहते हैं ।

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

अन्य समाधानों ने शब्दकोश सामान्य संरचना की सामान्य दुनिया के भीतर इसे करने पर ध्यान केंद्रित किया है, लेकिन मुझे शीर्ष-स्तरीय फ़ंक्शन के बारे में सोचना आसान लगता है। उदाहरण के लिए, हम इसे लिख सकते हैं, जहाँ K प्रमुख प्रकार है, V1 प्रारंभिक शब्दकोष का मान प्रकार है, और V2 अंत शब्दकोश का मूल्य प्रकार है:

func mapValues<K,V1,V2>(d1:[K:V1], closure:(V1)->V2) -> [K:V2] {
    var d2 = [K:V2]()
    for (key,value) in zip(d1.keys, d1.values.map(closure)) {
        d2.updateValue(value, forKey: key)
    }
    return d2
}

यहां इसे कॉल करने का एक सरल उदाहरण है, बस यह साबित करने के लिए कि सामान्य समय में संकलन समय पर खुद को हल करता है:

let d : [String:Int] = ["one":1, "two":2]
let result = mapValues(d) { (i : Int) -> String in String(i) }

हमने एक शब्दकोष के साथ शुरुआत की [String:Int] और [String:String]एक क्लोजर के माध्यम से पहले शब्दकोश के मूल्यों को बदलकर एक शब्दकोश के साथ समाप्त हुआ ।

(संपादित करें: अब मैं देखता हूं कि यह एयरस्पीड वेलोसिटी के समाधान के रूप में प्रभावी रूप से समान है, सिवाय इसके कि मैंने एक शब्दकोश इनिशियलाइज़र के अतिरिक्त लालित्य को नहीं जोड़ा है जो एक शब्दकोश को एक ज़िप अनुक्रम से बाहर कर देता है।)


1

स्विफ्ट 3

उपयोग:

let bob = ["a": "AAA", "b": "BBB", "c": "CCC"]
bob.mapDictionary { ($1, $0) } // ["BBB": "b", "CCC": "c", "AAA": "a"]

एक्सटेंशन:

extension Dictionary {
    func mapDictionary(transform: (Key, Value) -> (Key, Value)?) -> Dictionary<Key, Value> {
        var dict = [Key: Value]()
        for key in keys {
            guard let value = self[key], let keyValue = transform(key, value) else {
                continue
            }

            dict[keyValue.0] = keyValue.1
        }
        return dict
    }
}

1

मैं केवल मानों को मैप करने की कोशिश कर रहा हूं (संभवतः उनके प्रकार को बदल रहा है), इस एक्सटेंशन को शामिल करें:

extension Dictionary {
    func valuesMapped<T>(_ transform: (Value) -> T) -> [Key: T] {
        var newDict = [Key: T]()
        for (key, value) in self {
            newDict[key] = transform(value)
        }
        return newDict
    }
}

आपके पास यह शब्दकोश है:

let intsDict = ["One": 1, "Two": 2, "Three": 3]

एकल-पंक्ति मान परिवर्तन तब इस तरह दिखता है:

let stringsDict = intsDict.valuesMapped { String($0 * 2) }
// => ["One": "2", "Three": "6", "Two": "4"]

बहु-रेखा मान परिवर्तन तब इस तरह दिखता है:

let complexStringsDict = intsDict.valuesMapped { (value: Int) -> String in
    let calculationResult = (value * 3 + 7) % 5
    return String("Complex number #\(calculationResult)")
}
// => ["One": "Complex number #0", "Three": ...

0

एक और दृष्टिकोण mapएक शब्दकोश के लिए है reduce, और जहां कार्य keyTransformऔर valueTransformकार्य हैं।

let dictionary = ["a": 1, "b": 2, "c": 3]

func keyTransform(key: String) -> Int {
    return Int(key.unicodeScalars.first!.value)
}

func valueTransform(value: Int) -> String {
    return String(value)
}

dictionary.map { (key, value) in
     [keyTransform(key): valueTransform(value)]
}.reduce([Int:String]()) { memo, element in
    var m = memo
    for (k, v) in element {
        m.updateValue(v, forKey: k)
    }
    return m
}

यह एक अवधारणा थी तब वास्तविक कोड, लेकिन फिर भी, मैंने इसे रनिंग कोड में विस्तारित किया।
NebulaFox 15

0

स्विफ्ट 3 मैंने इसका इस्तेमाल किया,

func mapDict(dict:[String:Any])->[String:String]{
    var updatedDict:[String:String] = [:]
    for key in dict.keys{
        if let value = dict[key]{
            updatedDict[key] = String(describing: value)
        }
    }

   return updatedDict
}

उपयोग:

let dict:[String:Any] = ["MyKey":1]
let mappedDict:[String:String] = mapDict(dict: dict)

संदर्भ

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