Given a struct Parameter
which has a name
and a value
property, I would like to add a Subscript to an Array which let me return the value of the first found element whose name matches the subscript parameter.
Usage example:
let params = [Parameter("firstName", "John"), Parameter("lastName", "Doh")]
let value = params["firstName"] // returns "John"
Edit: The compiler error occurs only, if I define the array as follows:
let params: [ParameterType] = [Parameter("firstName", "John"), Parameter("lastName", "Doh")]
let value = params["firstName"] // returns "John"
Now, when I implement it, I get a compiler error:
error: ambiguous reference to member 'subscript'
let value = params["firstName"] // returns "John"
^~~~~~
note: found this candidate
public subscript (index: Int) -> Element { get set }
note: found this candidate
public subscript (name: String) -> String? { get set }
note: found this candidate
public subscript (bounds: Range<Self.Index>) -> MutableSlice<Self> { get set }
note: found this candidate
public subscript (position: Self.Index) -> Self.Base.Generator.Element { get }
So, why is this ambiguous?
That doesn't make sense to me.
Note: all the other candidates are implementations for struct Array defined within the standard library.
For example how is
subscript (index: Int) -> Element
and
subscript (name: String) -> String?
ambiguous?
I suspect, the error messages are just confusing, and the true error is because type parameter Element
is not fully specified. Anyway, how could this be implemented?
This is my attempt to implement it.
I am aware, that - due to the current limitations in the language - the subscript implementation is actually defined for a set of Element types conforming to ParameterType
:
public protocol ParameterType {
init(_ name: String, _ value: String)
var name: String { get }
var value: String { get }
}
public extension Array where Element: ParameterType {
subscript(name: String) -> String? {
get {
let a = self.filter { $0.name == name }
if let e = a.first {
return e.value
} else {
return nil
}
}
set(newValue) {
let i = self.index { $0.name == name }
if let index = i {
if let value = newValue {
self[index] = Element(name, value)
} else {
self.remove(at:index)
}
} else {
if let value = newValue {
let param = Element(name, value)
self.append(param)
} else { /* */ }
}
}
}
}
And here is one concrete type conforming to ParameterType
:
public struct Parameter: ParameterType {
public init(_ name: String, _ value: String) {
self.name = name
self.value = value
}
public var name: String
public var value: String
}
Note:
Constraining the array to a class type doesn't solve it: the same errors will be emitted.