I have this simple code, what I am trying to do is removing nil
values from [Any?]
and make an [Any]
. But compactMap does not work!
let optionalString: String? = nil
let optionalInt: Int? = nil
let customTuple = (optionalString, "a", "b", "c", optionalInt)
let arrayOfChildren: [Mirror.Child] = Array(Mirror(reflecting: customTuple).children)
let arrayOfChildrenValues: Array<Any?> = arrayOfChildren.map({ element in element.value })
let compactArrayOfChildrenValues: Array<Any> = arrayOfChildrenValues.compactMap({ element in element })
print(compactArrayOfChildrenValues)
The printed result:
[nil, "a", "b", "c", nil]
Test:
For example, the following simple test would work! But my code in top does not! What I am missing here?
let optionalString: String? = nil
let optionalInt: Int? = nil
let array: [Any?] = [optionalString, "Hello, world!", optionalInt, 1000]
print(array.compactMap({ element in element }))
result:
["Hello, world!", 1000]
Another Test:
let optionalString: String? = nil
let optionalInt: Int? = nil
let arrayOfChildrenValues: Array<Any?> = [optionalString, "a", "b", "c", optionalInt]
let compactArrayOfChildrenValues: Array<Any> = arrayOfChildrenValues.compactMap({ element in element })
print(compactArrayOfChildrenValues)
result:
["a", "b", "c"]
CodePudding user response:
Firstly, change the type of arrayOfChildrenValues
to [Any]
, so it is no longer optional, since \.value
doesn't return an optional.
Now - you want to see if the element of type Any
is an optional. To do this, you can try match it to Optional<Any>.none
.
Change the following parts:
let arrayOfChildrenValues: [Any] = arrayOfChildren.map(\.value)
let compactArrayOfChildrenValues: [Any] = arrayOfChildrenValues.filter { element in
switch element {
case Optional<Any>.none: return false
default: return true
}
}
Other ways
Here is another way:
let arrayOfChildrenValues: [Any] = arrayOfChildren.map(\.value)
let compactArrayOfChildrenValues: [Any] = (arrayOfChildrenValues as [Any?]).compactMap { $0 }
However, there is another method with some strange behaviour happening, I'm not sure I can fully understand.
Here's the code:
let arrayOfChildrenValues: [Any?] = arrayOfChildren.map(\.value)
let compactArrayOfChildrenValues: [Any] = arrayOfChildrenValues.compactMap { $0 }
The reason why this is strange is because all the other methods work with arrayOfChildrenValues
being expressed like { element in element.value }
, however this only works when using key-paths. Perhaps it's because the return type is inferred as [Any?]
, so it does a cast similar to the example above?
Why does your other example work?
The reason why compactMap
doesn't work on [Any]
is because a value of type Any
cannot be not nil
, since it is not an optional.
You can test this, by trying compactMap
on [Any]
instead of [Any?]
. Modify your other example, as so:
let anyArray: [Any] = array.map { $0 as Any }
print(anyArray.compactMap({ element in element }))
Notice this prints [nil, Optional("Hello, world!"), nil]
- and has also wrapped all our values in an optional because in reality this is still of type [Any?]
, 'disguised' as [Any]
.