Search code examples
arraysdictionaryswift3extend

Swift 3 - extend an Array of Dictionary<String, Any>


I have this array of dictionaries:

var dicts = [["key1": "value1", "key2": "value2"], ["key1": "value3", "key2": "value4"]]

How should I extend Array so that I have this function:

dicts.values(of: "key1") // result: ["value1", "value3"]

I try this:

extension Array where Iterator.Element == [String: Any] { // ERROR!

    func values(of key: String) -> [Any]? {
        var result: [Any]?
        for value in self {
            let val = value as! Dictionary<String, Any>
            for k in val.keys {
                if k == key {
                    if result == nil {
                        result = [Any]()
                    }
                    result?.append(val[k])
                    break
                }
            }
        }
        return result
    }
}

But I always have error marked at extension Array declaration:

error: same-type requirement makes generic parameter 'Element' non-generic

What should I do to fix this error?

Is it right to iterate Array with for value in self?

Thanks


Solution

  • extension Sequence where Iterator.Element == [String: Any] {
    
        func values(of key: String) -> [Any]? {
            var result: [Any] = []
            for value in self {
                let val = value 
                for (k, v) in val {
                    if k == key {
                        result.append(v)
                        break
                    }
                }
            }
            return result.isEmpty ? nil : result
        }
    }
    
    var dicts: [[String: Any]] = [["key1": "value1", "key2": "value2"], ["key1": "value3", "key2": "value4"]]
    dicts.values(of: "key1")   // ["value1", "value3"]
    

    or shorter version:

    extension Sequence where Iterator.Element == [String: Any] {
    
        func values(of key: String) -> [Any]? {
            let result: [Any] = reduce([]) { (result, dict) -> [Any] in
                var result = result
                result.append(dict.filter{ $0.key == key }.map{ $0.value })
                return result
            }
            return result.isEmpty ? nil : result
        }
    }
    
    var dicts: [[String: Any]] = [["key1": "value1", "key2": "value2"], ["key1": "value3", "key2": "value4"]]
    dicts.values(of: "key1")
    

    and without reduce functionality:

    extension Sequence where Iterator.Element == [String: Any] {
    
        func values(of key: String) -> [Any]? {
            var result: [Any] = []
            forEach {
                result.append($0.filter{ $0.key == key }.map{ $0.value })
            }
            return result.isEmpty ? nil : result
        }
    }
    
    var dicts: [[String: Any]] = [["key1": "value1", "key2": "value2"], ["key1": "value3", "key2": "value4"]]
    dicts.values(of: "key1")