Check array of optionals for nil - swift

I have a number of optionals and would like to efficiently check to see if any of them is nil.
Ideally...
if contains([optional1, optional2, optional3], nil) { /* foo */ }
But swift won't let me do this. (Type 'S.Generator.Element -> L' does not conform to protocol 'NilLiteralConvertible')
And optionals can't be AnyObjects so I can't cast it to an NSArray either (to use .containsObject).
I could just write a for loop but that seems like bad style. Any suggestions?

There are two contains() functions:
func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool
func contains<S : SequenceType, L : BooleanType>(seq: S, predicate: #noescape (S.Generator.Element) -> L) -> Bool
The first one takes an element as the second argument, but
requires that the elements conform to the Equatable
protocol, which is not the case for optionals.
The second one takes a predicate as second argument, and that
can be used here:
let optArray : [Int?] = [1, nil, 2]
if contains(optArray, { $0 == nil } ) {
}
But any solution must traverse the array until a nil element is
found, so the above solution may be better readable but is not
necessarily more efficient than a for-loop.

Related

Why are the arguments to this function still a tuple?

I have an extension to Dictionary that adds map, flatMap, and filter. For the most part it's functional, but I'm unhappy with how the arguments to the transform and predicate functions must be specified.
First, the extension itself:
extension Dictionary {
init<S:SequenceType where S.Generator.Element == Element>(elements: S) {
self.init()
for element in elements {
self[element.0] = element.1
}
}
func filter(#noescape predicate:(Key, Value) throws -> Bool ) rethrows -> [Key:Value] {
return [Key:Value](elements:try lazy.filter({
return try predicate($0.0, $0.1)
}))
}
}
Now then, since the predicate argument is declared as predicate:(Key, Value), I would expect the following to work:
["a":1, "b":2].filter { $0 == "a" }
however, I have to actually use:
["a":1, "b":2].filter { $0.0 == "a" }
This is kind of confusing to use since the declaration implies that there are two arguments to the predicate when it's actually being passed as a single tuple argument with 2 values instead.
Obviously, I could change the filter function declaration to take a single argument (predicate:(Element)), but I really prefer it to take two explicitly separate arguments.
Any ideas on how I can actually get the function to take two arguments?
When you are using closures without type declaration, the compiler has to infer the type. If you are using only $0 and not $1, the compiler thinks that you are declaring a closure with only one parameter.
This closure then cannot be matched to your filter function. Simple fix:
let result = ["a":1, "b":2].filter { (k, _) in k == "a" }
Now also remember that tuples can be passed to functions and automatically match the parameters:
func sum(x: Int, _ y: Int) -> Int {
return x + y
}
let params = (1, 1)
sum(params)
Then the behavior with ["a":1, "b":2].filter { $0.0 == "a" } can be explained by type inferring. There are two possibilities and the compiler just chose the wrong one because it thought you want to have a closure with one argument only - and that argument had to be a tuple.
You can add a comparison function to allow you to compare a Dictionary.Element and a Key
func ==<Key: Hashable,Value>(lhs:Dictionary<Key,Value>.Element, rhs:Key) -> Bool {
return lhs.0 == rhs
}

Does an equality operator imply forced unwrapping?

Note the last two lines:
var optionalString: String? // nil
optionalString == "A" // false
//optionalString! == "A" // EXC_BAD_INSTRUCTION
optionalString = "A" // “A”
optionalString == "A" // true
optionalString! == "A" // true
Does that mean that we are not required to unwrap an optional when comparing?
This is the definition of the == operator that looks like it gets used in this case:
public func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
You see that both the first argument (lhs) and the second argument (rhs) have to be of the same type, T?. Since the first argument (optionalString) is String?, I think the second argument is cast to String? too, which makes the comparison work.
I think this proves the idea:
func testCasting<T: Equatable>(a: T?, b: T?) {
print(a, b) // Optional("A") Optional("A")
}
var optionalString: String? = "A"
testCasting(optionalString, b: "A")
In the second argument you pass a literal A that gets wrapped in an optional to make the types check. The code compiles and runs.
Note that this is completely different from unwrapping the first argument implicitly. That wouldn’t be a safe operation and would undermine the whole point of optionals.
Apparently one can compare any optional to nil.
optionalString == nil
Which is true if an optional is nil now.
Even this works:
if "A" != nil {
print("A")
}

Recursively parse a CollectionType

I'm writing a recursive descent parser. I'd like my parser to work on any (or at least "many") Collections of UInt8 (e.g., not only Swift.Array)
func unpack<T: CollectionType where T.Generator.Element == UInt8>(t: T) {
let m = t.dropFirst()
//[do actual parsing here]
unpack(m)
}
However:
error: cannot invoke 'unpack' with an argument list of type '(T.SubSequence)'
note: expected an argument list of type '(T)'
This is puzzling because:
dropFirst returns Self.SubSequence
CollectionType.SubSequence is SubSequence : Indexable, SequenceType = Slice<Self>
Slice is CollectionType.
Therefore, m should be CollectionType.
However for some reason, this doesn't work. How do I define unpack so it can be recursively passed subsequences?
There is no CollectionType in Swift anymore. Both Array and ArraySlice adopt Sequence. And the dropFirst() method which you use is declared in Sequence. So you can make recursive generic function like this:
func unpack<T: Sequence>(t: T) where T.Element == UInt8 {
let m = t.dropFirst()
//[do actual parsing here]
unpack(m)
}

Swift error comparing two arrays of optionals

I get a compilation error in the next Swift code
var x:Array<Int?> = [1,2]
var y:Array<Int?> = [1,2]
if x == y { // Error
}
If both arrays are Array<Int> it works fine, but if at least one of them is optional it throws an error like the next:
Binary operator '==' cannot be applied to two Array<Int?> operands
I filed a bug report months ago but I had no answer. It still occurs in Swift 1.2.
Why is this happening?
The issue here is the distinction between something having an == operator, versus something being “equatable”.
Both Optional and Array have an == operator, that works when what they contain is equatable:
// if T is equatable, you can compare each entry for equality
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
// if T is equatable, you can compare the contents, if any, for equality
func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
let i: Int? = 1
let j: Int = 1
i == j // fine, Int is Equatable
["a","b"] == ["a","b"] // and so is String
But they themselves do not conform to Equatable. This makes sense given you can put a non-equatable type inside them. But the upshot of this is, if an array contains a non-equatable type, then == won’t work. And since optionals aren’t Equatable, this is the case when you put an optional in an array.
You'd get the same thing if you tried to compare an array of arrays:
let a = [[1,2]]
let b = [[1,2]]
a == b // error: `==` can’t be applied to `[Array<Int>]`
If you wanted to special case it, you could write == for arrays of optionals as:
func ==<T: Equatable>(lhs: [T?], rhs: [T?]) -> Bool {
if lhs.count != rhs.count { return false }
for (l,r) in zip(lhs,rhs) {
if l != r { return false }
}
return true
}
For a counter-example, since Set requires its contents to be hashable (and thus equatable), it can be equatable:
let setarray: [Set<Int>] = [[1,2,3],[4,5,6]]
setarray == [[1,2,3],[4,5,6]] // true

Is there a way to test whether an Array's type is optional?

Let's say I want to write a method on Array that either returns a copy of the array if its type is non-optional, or a subarray of unwrapped values if its type is an optional. To do this, I think I need to be able to test whether the Array's type T is an optional type or not. For example, this just returns a copy of the array in either case:
extension Array {
func unwrapped() -> Array<T> {
return filter({
var x: T? = $0
return x != nil
})
}
}
I understand that if I know I have an array of optionals I could just use filter() and map():
let foo: [String?] = [nil, "bar", "baz"]
let bar: [String] = foo.filter({ $0 != nil }).map({ $0! })
I'm not looking for a solution to that specific problem. Rather I'm wondering if there's a way to determine in an Array extension if its type is optional, which could be useful in a number of different convenience methods.
One possible solution is to use global functions instead of extensions; then you can overload with two definitions, one for non-optional and one for optional types:
func unwrapped<T>(a: [T]) -> [T] { return a }
func unwrapped<T>(a: [T?]) -> [T] {
return a.filter { $0 != nil }.map { $0! }
}
unwrapped([1, 2, 3, 4, 5]) // -> [1, 2, 3, 4, 5]
unwrapped([1, 2, 3, nil, 5]) // -> [1, 2, 3, 5]
I'm unsure whether this is guaranteed to work; it would be interesting if someone can find a case where it breaks or somewhere in the Swift guides that says it's always correct.
See also the discussion in this question.
Use reduce:
let unwrapped = foo.reduce([String]()) {
if let bar = $1 as String? { return $0 + [bar] }
return $0
}
That will return a copy if the original is non-optional, or a subarray of non-nil unwrapped values if the original is optional. You don't need to know if the array contains optionals in order to do this.
If an array contains at least one value, you can determine if that value is an optional like this:
extension Array {
func optTest() {
switch reflect(self[0]).disposition {
case .Optional:
println("I am an optional")
default:
break
}
}
}
You would only need to test the first value. But I haven't figured out how to test an empty array - I suppose you could add an element to it and then test that element...
To do this, I think I need to be able to test whether the Array's type T is an optional type or not.
No, you don't have to - just use flatMap!
let foo: [String?] = [nil, "bar", "baz"]
let foo2: [String] = ["bar", "baz"]
foo.flatMap{$0}
foo2.flatMap{$0}
// yield both ["bar", "baz"] and are of type Array<String>
The Array struct can be extended to return its generic type, and then a protocol can be used to test for a typeless Optional.
The example is made for Swift 2, but I believe it should work similarly on previous versions.
protocol OptionalProtocol {}
extension Optional : OptionalProtocol {}
extension Array {
func elementType() -> Any.Type {
return Element.self
}
}
[String]().elementType() is OptionalProtocol.Type // false
[String?]().elementType() is OptionalProtocol.Type // true
Specific to Swift 2, the Array extension can be foregone entirely since the typealias allow to access the Element type:
protocol OptionalProtocol {}
extension Optional : OptionalProtocol {}
[String]().dynamicType.Element.self is OptionalProtocol.Type // false
[String?]().dynamicType.Element.self is OptionalProtocol.Type // true