Argent
Argent

Reputation: 390

Swift Error: Can't invoke (..) with an argument list of type (([String) -> (Int, String))

I wanted to create an Array extension that transforms an array into a Dictionary with generic Key/Value types.

This is my code:

extension Array  {
    func transformToDictionary<K: Hashable, V>(@noescape mappingFunction: (Array.Generator.Element) -> (K, V)) -> [K: V] {
        var dict: [K: V] = [:]
        for el in self {
            let result = mappingFunction(el)
            dict[result.0] = result.1
        }
        return dict
    }
}

This works for some cases like:

let stringArray = ["String1", "String2"]
stringArray.transformToDictionary({ (element: String) -> (Int, String) in
    return (element.hashValue, element)
})

But doesn't work in another case like:

let nestedStringArray = [["String1"], ["String2"]]
stringArray.transformToDictionary({ (element: [String]) -> (Int, String) in
     return (element.first!.hashValue, element.first!)
})

This will lead to a compiler error saying:

Can't invoke 'transformToDictionary' with an argument list of type (([String) -> (Int, String))

Did I do anything wrong here or is this a Swift compiler bug?

Upvotes: 3

Views: 2623

Answers (2)

Tom Elliott
Tom Elliott

Reputation: 1926

In your second case you're trying to perform the nested array operation on the stringArray object. Change stringArray to nestedStringArray here and it should work (at least it did in my Playground):

let nestedStringArray = [["String1"], ["String2"]]
nestedStringArray.transformToDictionary({ (element: [String]) -> (Int, String) in
     return (element.first!.hashValue, element.first!)
})

Upvotes: 1

Steve Wilford
Steve Wilford

Reputation: 9002

First let's inspect the first example:

let stringArray = ["String1", "String2"]
stringArray.transformToDictionary({ (element: String) -> (Int, String) in
    return (element.hashValue, element)
})

stringArray is an Array of String objects and the transformToDictionary method expects a String (element) and returns a tuple of Int and String. This works as you have pointed out.

Now take the second example:

let nestedStringArray = [["String1"], ["String2"]]
stringArray.transformToDictionary({ (element: [String]) -> (Int, String) in
    return (element.first!.hashValue, element.first!)
})

The nestedStringArray is not used and the transformToDictionary method is actually being called on stringArray.

If we fix that:

let nestedStringArray = [["String1"], ["String2"]]
nestedStringArray.transformToDictionary({ (element: [String]) -> (Int, String) in
    return (element.first!.hashValue, element.first!)
})

We will still get the error you mentioned:

Cannot invoke 'transformToDictionary' with an argument list of type '(([String]) -> (Int, String))'

The reason for this is that the 2nd item in the returned tuple is a String, but the input value is a string array ([String]).

This usage will work with an array of strings:

let nestedStringArray = [["String1"], ["String2"]]
nestedStringArray.transformToDictionary({ (element: [String]) -> (Int, [String]) in
    return (0, element)
})

Upvotes: 0

Related Questions