2015-10-30 17 views
11

で汎用パラメータに制約を追加します。私はこの機能を持っている拡張

func flatten<Key: Hashable, Value>(dict: Dictionary<Key, Optional<Value>>) -> Dictionary<Key, Value> { 
    var result = [Key: Value]() 
    for (key, value) in dict { 
     guard let value = value else { continue } 
     result[key] = value 
    } 
    return result 
} 

あなたが見ることができるように、それは(オプションなし)[Key: Value]一つに[Key: Value?]辞書を変換します。

Optionalの値を持つクラスに対してのみ、新しいメソッドでDictionaryクラスを拡張したいと考えましたが、辞書の汎用パラメータに制約を追加できません。

extension Dictionary where Value: Optional<Any> { 
    func flatten() -> [Key: Any] { 
     var result = [Key: Any]() 
     for (key, value) in self { 
      guard let value = value else { continue } 
      result[key] = value 
     } 
     return result 
    } 
} 

しかし、エラーで失敗します:

これは私が試したものですあなたが正確な型を指定することはできませんので

// make sure only `Optional` conforms to this protocol 
protocol OptionalEquivalent { 
    typealias WrappedValueType 
    func toOptional() -> WrappedValueType? 
} 

extension Optional: OptionalEquivalent { 
    typealias WrappedValueType = Wrapped 

    // just to cast `Optional<Wrapped>` to `Wrapped?` 
    func toOptional() -> WrappedValueType? { 
    return self 
    } 
} 

extension Dictionary where Value: OptionalEquivalent { 
    func flatten() -> Dictionary<Key, Value.WrappedValueType> { 
    var result = Dictionary<Key, Value.WrappedValueType>() 
    for (key, value) in self { 
     guard let value = value.toOptional() else { continue } 
     result[key] = value 
    } 
    return result 
    } 
} 

let a: [String: String?] = ["a": "a", "b": nil, "c": "c", "d": nil] 
a.flatten() //["a": "a", "c": "c"] 

Type 'Value' constrained to non-protocol type 'Optional<Any>' 

答えて

16

は遊び場でこのコードを試してみてくださいプロトコル拡張のwhere句では、ちょうどを検出する可能性がありますタイプは、Optionalがプロトコル(例:OptionalEquivalent)に一意に従うようにすることです。

Optionalの包まれた値の型を取得するためには、私は、カスタムプロトコルOptionalEquivalentにtypealias WrappedValueTypeを定義し、その後、あなたはフラット化方式でタイプを取得することができ、WrappedからWrappedValueType assgin、オプションの拡張を作りました。 sugarCast方法は、単に、使用guardステートメントを有効にするために、(まったく同じことである)Wrapped?Optional<Wrapped>をキャストしていることを

注意。私は&を簡素化しているロブ・ネイピアさんのコメントに

UPDATE

おかげでsugarCast()メソッドを名前を変更し、それをより理解しやすくするためのプロトコルの名前を変更しました。

+0

をより簡単に'sugarCast()'を 'return self'として実装してください。私は個人的にこのプロトコルを 'OptionalConvertible'と' sugarCast() '' toOptional() 'と呼ぶことをお勧めしますが、あなたのやり方も問題ありません。 –

+0

あなたが正しいです、私は自分のコードを更新します – cezheng

+0

これは、一般的な制限を回避する非常に創造的な方法です。私はおそらくもっと多くの場所でそれを使用します。ありがとう – redent84

2

これは簡単な方法で実行できます。これは、スウィフト4で動作します:

extension Dictionary { 
    func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> { 
     return filter { $1 != nil }.mapValues { $0! } 
    } 
} 

あなたは高階関数を使用することを好むか、あなたが同様にこれを行うことができスウィフトの以前のバージョンとの互換性を必要としない場合は、次のことができます

extension Dictionary { 
    func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> { 
     var result: [Key: Wrapped] = [:] 
     for (key, value) in self { 
      guard let value = value else { continue } 
      result[key] = value 
     } 
     return result 
    } 
} 
+1

受け入れられた答えよりも簡単で読みやすい – moskis

関連する問題