Extension for Array Where Element Is Optional

Array extension where Element is Optional and the the return type is Wrapped

I am not sure what you mean with Wrapped<Element> but since you need to return something why not use a closure for the return value, like this function to get the element at a specific index

extension Array  {
func value<T>(at index: Int, emptyAction: () -> T) -> T where Element == T? {
if let value = self[index] {
return value
}
return emptyAction()
}
}

Example

var test = [String?]()
test.append("ABC")
test.append("DEF")
test.append(nil)

for i in 0..<test.count {
print(test.value(at: i, emptyAction: { "<empty>" }))
}

Outputs

ABC   
DEF
<empty>

Extension for Array where Element is Optional

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?

How to extend protocol Optional, where Wrapped item is Array of Equatable generic elements?

Cristik provided a good solution. An alternative is to write an extension to an optional collection of equatable elements:

extension Optional where Wrapped: Collection, Wrapped.Element: Equatable {
func foo() { }
}

This would be applicable to arrays, array slices, and other collections.

Depending on what the extension does, you may want to use a combination of Collection, MutableCollection, BidirectionalCollection, RandomAccessCollection.

Extension optional Array with Optional Element. Is it even possible?

From my understanding, it should work as you did, but one never knows what happens in the swift compiler world (and especially it's error messages).

Anyway, you can circumvent digging deeper into Wrapped.Element.Wrapped by specifyig the Wrapped.Element more precisely to be an Optional<FooProtocol>:

protocol FooProtocol {}
class Foo : FooProtocol {}

extension Optional where
Wrapped: Collection, //OK
Wrapped.Element == Optional<FooProtocol> // still good
{
var unfied: Wrapped.Element // Should be 'Foo' if self is '[Foo?]?' {
{
return 1 == 0 ? nil : Foo()
}
}

Extend an Array of Optional Equatables?

As mentioned in the comments you can't do:

extension Array where Element == Optional<Equatable>

But you can create a generic function in the Array extension:

extension Array {
func foo<T: Equatable>() where Element == Optional<T> {
...
}
}

Function ArrayOptionalT - OptionalArrayT

Try this:

protocol OptionalType {
typealias W
var optional: W? { get }
}

extension Optional: OptionalType {
typealias W = Wrapped
var optional: W? { return self }
}

extension Array where Element: OptionalType {
func unwrap() -> [Element.W]? {
return reduce(Optional<[Element.W]>([])) { acc, e in
acc.flatMap { a in e.optional.map { a + [$0] } }
}
}
}

And then,

let a: [Int?] = [1,   2, 3]
let b: [Int?] = [1, nil, 3]

a.unwrap() // ==> [1, 2, 3]
b.unwrap() // ==> nil

Is there a way to restrict an Array extension to arrays whose elements are NOT optionals?

This turned out to be an XY problem. The reason why you wanted to do this seems to just be that you cannot distinguish a nil returned by your subscript, and an actual nil in the array.

Well, you can. When you use the subscript on an array of optionals, it would return a double optional. e.g. for [String?], it would return String??.

Here's an example of determining all 3 cases with a switch. You can use if case if you just want to check one of them.

switch array[safe: 1] {
case .none: // subscript returned nil
break
case .some(nil): // subscript returned non-nil, but that index in the array contained nil
break
case .some(let x): // subscript returned non-nil, and that index contained x
break
}

So there is really no need to restrict your subscript to non-Optional arrays. Swift is not designed to allow you to do that anyway.


Just for a bit of fun though, here's how you can abuse Swift's features to make a warning:

extension Array {
subscript<T>(safe index: Int) -> Element? where Element == T {
return indices ~= index ? self[index] : nil
}

@available(swift, deprecated: 1, message: "Do not use this on arrays of optionals!")
subscript<T>(safe index: Int) -> Element? where Element == T? {
return indices ~= index ? self[index] : nil
}
}

I added an extra subscript that only works on arrays of optionals, but it is marked deprecated since Swift 1 (lol). I added an extra type parameter to the other overload too, so that overload resolution would consider them equally. Otherwise it would always prefer the one with fewer type parameters - we want it to decide based on the generic constraints instead.

Now when you do array[safe: 1], overload resolution chooses the one marked deprecated, and produces a warning.

How to create an extension on Swift Array to replace nil with a value?

The use of compactMap is incorrect here. compactMap turns a [T] to another [U] - it is the mapping function that is allowed to return an optional ((T) -> U?), and it removes any element for which this function returns nil. This property can be used to remove nils from a [T?] and get a [T] by passing it { $0 }, but that's not what you are trying to do with the [T?] in this case.

What you want here is to turn a [T?] to [T], and you have a one-to-one mapping, which can be done with a map like this:

extension Sequence {
func replacingNil<T>(with element: T) -> [T] where Element == T? {
self.map { $0 ?? element }
}
}

Swift: Extension on [SomeTypeT?] to produce [T?] possible?

I don't know if there is a simpler solution now, but you can use the same “trick” as in How can I write a function that will unwrap a generic property in swift assuming it is an optional type? and Creating an extension to filter nils from an Array in Swift, the idea goes back to this Apple Forum Thread.

First define a protocol to which all optionals conform:

protocol OptionalType {
associatedtype Wrapped
var asOptional: Wrapped? { get }
}

extension Optional : OptionalType {
var asOptional: Wrapped? {
return self
}
}

Now the desired extension can be defined as

extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {
var values: [Element.Wrapped.NumberType?] {
return self.map( { $0.asOptional?.value })
}
}

and that works as expected:

let arr = [SomeType(value: 123), nil, SomeType(value: 456)]
let v = arr.values

print(v) // [Optional(123), Optional(456)]
print(type(of: v)) // Array<Optional<Int>>


Related Topics



Leave a reply



Submit