20

मैं एक छोटे से flatMap चारों ओर संदेह में हूँ से optionals को फिल्टर करने की तेज flatMap का उपयोग कैसे करें (स्विफ्ट 1.2 करने के लिए जोड़ा)एक सरणी

मैं कुछ वैकल्पिक प्रकार के उदाहरण के लिए एक सरणी है कहो

let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5] 

स्विफ्ट 1.1 में मैं एक फिल्टर इस तरह एक नक्शे के द्वारा पीछा किया करते हैं:

let filtermap = possibles.filter({ return $0 != nil }).map({ return $0! }) 
// filtermap = [1, 2, 3, 4, 5] 

मैं इस flatMap का उपयोग कर कुछ तरीके करने का प्रयास कर रहा है:

var flatmap1 = possibles.flatMap({ 
    return $0 == nil ? [] : [$0!] 
}) 

और

var flatmap2:[Int] = possibles.flatMap({ 
    if let exercise = $0 { return [exercise] } 
    return [] 
}) 

मैं अंतिम अनुमोदन पसंद करता हूं ओच (क्योंकि मुझे मजबूर अनचाहे $0! नहीं करना है ... मैं इनके लिए डरता हूं और उन्हें हर कीमत से बचता हूं) सिवाय इसके कि मुझे ऐरे प्रकार निर्दिष्ट करने की आवश्यकता है।

क्या कोई वैकल्पिक विकल्प है जो संदर्भ के प्रकार को आंकड़े करता है, लेकिन मजबूर अनचाहे नहीं है?

+0

आप शायद मतलब स्विफ्ट 1.2 बनाम 1.1, वहाँ अभी तक कोई स्विफ्ट 1.3 (या मैं कुछ याद किया?) –

+0

ओह, हाँ लगता है कि मैं मेरे दिमाग पर Xcode 6.3 था ... अद्यतन प्रश्न - धन्यवाद! – MathewS

+1

हालांकि आप * नाइल्स को हटाने के लिए 'flatMap {$ 0}' का उपयोग कर सकते हैं, असली सवाल * आपको * चाहिए। यदि आप सावधान नहीं हैं, तो 'flatMap' बग का कारण बन सकता है, इसलिए मैं इसके बजाय [removeNils] (http://stackoverflow.com/a/38548106/35690) का उपयोग करने की सलाह देता हूं। – Senseful

उत्तर

33
स्विफ्ट 2 बी 1 के साथ

, तो आप बस

let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5] 
let actuals = possibles.flatMap { $0 } 

कर सकते हैं पहले के संस्करणों के लिए, आप निम्नलिखित विस्तार के साथ इस शिम कर सकते हैं:

extension Array { 
    func flatMap<U>(transform: Element -> U?) -> [U] { 
     var result = [U]() 
     result.reserveCapacity(self.count) 
     for item in map(transform) { 
      if let item = item { 
       result.append(item) 
      } 
     } 
     return result 
    } 
} 

एक चेतावनी (जो भी सच है स्विफ्ट 2 के लिए) यह है कि आपको ट्रांसफॉर्म के रिटर्न वैल्यू को स्पष्ट रूप से टाइप करने की आवश्यकता हो सकती है:

let actuals = ["a", "1"].flatMap { str -> Int? in 
    if let int = str.toInt() { 
     return int 
    } else { 
     return nil 
    } 
} 
assert(actuals == [1]) 

अधिक जानकारी के लिए, देखें http://airspeedvelocity.net/2015/07/23/changes-to-the-swift-standard-library-in-2-0-betas-2-5/

+0

मैंने फ्लैटमैप देखा लेकिन पता नहीं था कि आप इसका उपयोग वैकल्पिक विकल्पों को फ़िल्टर करने के लिए कर सकते हैं। स्विफ्ट 2 के लिए वास्तव में अच्छा जोड़ा! – MathewS

0

आप reduce इस्तेमाल कर सकते हैं:

let flattened = possibles.reduce([Int]()) { 
     if let x = $1 { return $0 + [x] } else { return $0 } 
    } 

तुम अब भी तरह के प्रकार की घोषणा कर रहे हैं, लेकिन यह थोड़ा कम निकला हुआ है।

+0

यह कम करने का चालाक उपयोग है, लेकिन (मेरे लिए) क्या हो रहा है यह देखने के लिए बहुत अधिक मानसिक अनचाहे लग रहा है। विचार के लिए धन्यवाद! – MathewS

14

मुझे अभी भी पहला समाधान पसंद है, जो केवल एक इंटरमीडिएट सरणी बनाता है। यह थोड़ा अधिक कॉम्पैक्ट प्रकार एनोटेशन के बिना और बिना मजबूर unwrapping संभव है के रूप में

let filtermap = possibles.filter({ $0 != nil }).map({ $0! }) 

लेकिन flatMap() लिखा जा सकता है:

var flatmap3 = possibles.flatMap { 
    flatMap($0, { [$0] }) ?? [] 
} 

बाहरी flatMap सरणी विधि

func flatMap<U>(transform: @noescape (T) -> [U]) -> [U] 
है

और आंतरिक flatMap समारोह

func flatMap<T, U>(x: T?, f: @noescape (T) -> U?) -> U? 

है यहाँ एक सरल प्रदर्शन की तुलना (रिलीज़ मोड में संकलित) है। यह दर्शाता है कि पहली विधि, तेजी से होता है लगभग एक कारक 10 की द्वारा:

let count = 1000000 
let possibles : [Int?] = map(0 ..< count) { $0 % 2 == 0 ? $0 : nil } 

let s1 = NSDate() 
let result1 = possibles.filter({ $0 != nil }).map({ $0! }) 
let e1 = NSDate() 
println(e1.timeIntervalSinceDate(s1)) 
// 0.0169369578361511 

let s2 = NSDate() 
var result2 = possibles.flatMap { 
    flatMap($0, { [$0] }) ?? [] 
} 
let e2 = NSDate() 
println(e2.timeIntervalSinceDate(s2)) 
// 0.117663979530334 
+0

धन्यवाद! यह देखते हुए कि फिल्टर/मानचित्र विकल्प तेज़ है, मैं इसके साथ रह सकता हूं (मेरे लिए) यह स्पष्ट हो रहा है कि क्या हो रहा है। स्विफ्ट 2 में – MathewS

+0

आप बस 'possibles.flatmap {$ 0}' पर जा सकते हैं। दस्तावेज़ देखें: 'सार्वजनिक func flatMap (@noescape ट्रांसफॉर्म: (Self.Generator.Element) फेंकता है -> टी?) Rethrows -> [टी] 'Flatmap एक' ऐरे 'देता है जिसमें मैपिंग' ट्रांसफॉर्म 'के गैर-शून्य परिणाम होते हैं 'आत्म' पर। –

+0

@DanielGalasko: हां, फिजकर (अब) स्वीकृत उत्तर में यही लिखता है। मुझे लगता है कि जब मैंने यह जवाब लिखा था तब उपलब्ध नहीं था। –

0

इस के बाद से कुछ मैं काफी एक बहुत मैं यह करने के लिए एक सामान्य समारोह की खोज कर रहा हूँ कर खत्म करने लगते है।

मैंने ऐरे में एक एक्सटेंशन जोड़ने की कोशिश की ताकि मैं possibles.unwraped जैसे कुछ कर सकूं लेकिन यह पता नहीं लगा सकता कि कैसे ऐरे पर विस्तार करना है। इसके बजाए एक कस्टम ऑपरेटर का उपयोग किया गया - यहां सबसे कठिन हिस्सा यह पता लगाने की कोशिश कर रहा था कि कौन सा ऑपरेटर चुनना है। अंत में मैंने >! को यह दिखाने के लिए चुना कि सरणी को > फ़िल्टर किया जा रहा है और फिर ! अनचाहे किया गया है।

let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5] 

postfix operator >! {} 

postfix func >! <T>(array: Array<T?>) -> Array<T> { 
    return array.filter({ $0 != nil }).map({ $0! }) 
} 

possibles>! 
// [1, 2, 3, 4, 5]