I have an array of String? like so
var myArray: [String?] = ["2", "banana", nil, "31"];
I have another array of String like so:
var myStringArray: [String] = ["2", "3"]
I wrote an extension for Array to return me an Optional if index is not valid (which avoids the whole app crashing), but I really only want it to return Optional if the Element is NOT optional, and just return Element if it is Optional
func safeIndex(_ index: Int) -> Element? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
If i run this against myArray the resulting type is String??
let item = myArray.safeIndex(1) // item is String??
let strItem = myStringArray.safeIndex(99) // strItem is String?
How do i change my extension such that it gives me String? for BOTH cases
Note: If i am explicit about my types, I can get what I want like so
extension Array where Element == String? {
func safeIndex(_ index: Int) -> String? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
}
However, this approach requires me to write a bunch of extensions for a bunch of types i might have in an array which is a massive meh.
This is kind of tricky but you can create an AnyOptional protocol that requires an associatedtype (Wrapped) and a computed property to return the optional type. Then you can return the element unwrapped if the index is valid otherwise return nil.
protocol AnyOptional {
associatedtype Wrapped
var optional: Optional<Wrapped> { get }
}
extension Optional: AnyOptional {
var optional: Optional<Wrapped> { self }
}
extension Collection {
subscript(safe index: Index) -> Element? {
indices.contains(index) ? self[index] : nil
}
}
extension Collection {
subscript(safe index: Index) -> Element.Wrapped? where Element: AnyOptional {
indices.contains(index) ? self[index].optional ?? nil : nil
}
}
var myArray: [String?] = ["2", "banana", nil, "31"]
var myStringArray: [String] = ["2", "3"]
let item = myArray[safe: 1] // item is String?
let strItem = myStringArray[safe: 99] // strItem is String?
I think what you want could be achieved with two extensions and a dummy protocol, which would constrain the array's Element. Something like that:
protocol OptionalType {}
extension Optional: OptionalType {}
extension Array where Element: OptionalType {
func safeIndex(_ index: Int) -> Element {
return self[index]
}
}
extension Array {
func safeIndex(_ index: Int) -> Element? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
}
Then this would work:
var myArray: [String?] = ["2", "banana", nil, "31"];
var myStringArray: [String] = ["2", "3"]
let item = myArray.safeIndex(1)
print(type(of: item)) // Optional<String>
let strItem = myStringArray.safeIndex(99)
print(type(of: strItem)) // Optional<String>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With