简体   繁体   中英

Array.map() produces '[T]', not the expected contextual result type '[String: Any?]'

I'm writing an extension to bridge the dictionary values between FirebaseDatabase and Eureka .

private extension Dictionary {
    func firebaseFriendlyDictionary() -> [String: Any?] {
        return self.map({ (key: String, value: Any?) -> (String, Any?) in
            if value is NSDate {
                return (key, (value as! NSDate).timeIntervalSince1970)
            }
            return (key, value)
        })
    }
}

But I get thrown this error when I try to build:

map produces '[T]', not the expected contextual result type '[String: Any?]'

Your problem lies with the fact, that map always returns an Array , even when applied on a Dictionary . Your error message basically means, that you declared your method as returning a Dicitonary , but the statement inside returns an Array ( [T] - means an Array with objects of some type T). In your case, the array returned by map will contain tuples (more about them here ). In this case it looks like a key value pair, but its not equivalent to a key-value pair inside a Dictionary. Basically, tuples enable you to return more than one value/object from method. You can think of them as of anonymous structures.

In my opinion, there is no need to use a map to accomplish what you need - the solution provided by Mr. Xcoder is the way to go.

If you really want to use something functional like map , the method you actually want is reduce .

I'll demonstrate. To make things as clear as possible, I think it will help if we separate out the transformation to which your values are being subjected into a function of its own:

func dateToSeconds(_ thing:Any?) -> Any? {
    guard let date = thing as? Date else {return thing}
    return date.timeIntervalSince1970
}

Okay, so here's our test dictionary:

let d1 : [String:Any?] = ["1":Date(), "2":"two", "3":15, "4":true]

Now we're ready to apply reduce . It passes two parameters into its function. The first is the "accumulator" where we keep building up the ultimate result, which in this case is another dictionary. The second is an element of the original dictionary, represented as a tuple of key-value pairs called key and value :

let d2 = d1.reduce([String:Any?]()) { (dict, tuple) in
    var dict = dict
    dict[tuple.key] = dateToSeconds(tuple.value)
    return dict
}

And when we examine d2 , we see that we have got the right answer:

d2 // ["3": {some 15}, "2": {some "two"}, "1": {some 1486228695.557882}, "4": {some true}]

I couldn't figure out how to fix that error, nor have I succeeded achieving the desired result with an extension or a map() , but I have an alternative solution to the problem, using a function :

Declaring Dictionary:

var date = NSDate()
var swiftDict:[String : Any?] = ["1":  date, "2": "two", "3": 15, "4": true]

Function:

func firebaseFriendlyDictionary(_ dict: [String : Any?]) -> [String : Any?]{
    var Dict = dict
    for (key, value) in dict
    {
        if (value is NSDate){
            Dict[key] = (value as! NSDate).timeIntervalSince1970
        }
    }
    return Dict
}

Usage:

swiftDict = firebaseFriendlyDictionary(swiftDict)

Testing:

Assuming that we have the date 2017-02-04 16:42:46 +0000 the output is 1486226566.2349629 , which is correct.

Why not mapping the Dictionary? As Losiowaty pointed in his excellent answer, map always returns an Array , in this case an Array of Tuples ( [T] ). In my opinion a map function is not needed in this context, plus it requires more code to accomplish.

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM