79

In Objective-C, when I have an array

NSArray *array;

and I want to check if it is not empty, I always do:

if (array.count > 0) {
    NSLog(@"There are objects!");
} else {
    NSLog(@"There are no objects...");
}

That way, there is no need to check if array == nil since this situation will lead the code to fall into the else case, as well as a non-nil but empty array would do.

However, in Swift, I have stumbled across the situation in which I have an optional array:

var array: [Int]?

and I am not being able to figure out which condition to use. I have some options, like:

Option A: Check both non-nil and empty cases in the same condition:

if array != nil && array!.count > 0 {
    println("There are objects")
} else {
    println("No objects")
}

Option B: Unbind the array using let:

if let unbindArray = array {
    if (unbindArray.count > 0) {
        println("There are objects!")
    } else {
        println("There are no objects...")
    }
} else {
    println("There are no objects...")
}

Option C: Using the coalescing operator that Swift provides:

if (array?.count ?? 0) > 0 {
    println("There are objects")
} else {
    println("No objects")
}

I do not like the option B very much, because I am repeating code in two conditions. But I am not really sure about whether options A and C are correct or I should use any other way of doing this.

I know that the use of an optional array could be avoided depending on the situation, but in some case it could be necessary to ask if it is empty. So I would like to know what is the way to do it the simplest way.


EDIT:

As @vacawama pointed out, this simple way of checking it works:

if array?.count > 0 {
    println("There are objects")
} else {
    println("No objects")
}

However, I was trying the case in which I want to do something special only when it is nil or empty, and then continue regardless whether the array has elements or not. So I tried:

if array?.count == 0 {
    println("There are no objects")
}

// Do something regardless whether the array has elements or not.

And also

if array?.isEmpty == true {
    println("There are no objects")
}

// Do something regardless whether the array has elements or not.

But, when array is nil, it does not fall into the if body. And this is because, in that case, array?.count == nil and array?.isEmpty == nil, so the expressions array?.count == 0 and array?.isEmpty == true both evaluate to false.

So I am trying to figure out if there is any way of achieve this with just one condition as well.

Daniel Storm
  • 18,301
  • 9
  • 84
  • 152
eze.scaruli
  • 1,207
  • 1
  • 13
  • 19
  • Is there a reason that you made the array optional? For table views and stuff I use non optional because (like you said) nil is the same as no objects and so by removing nil you reduce the complexity without losing any functionality. – Fogmeister Dec 21 '14 at 12:26
  • I think that option `A` is the most readable. That's only thing you should care fore. – Sulthan Dec 21 '14 at 14:30

8 Answers8

148

Updated answer for Swift 3 and above:

Swift 3 has removed the ability to compare optionals with > and <, so some parts of the previous answer are no longer valid.

It is still possible to compare optionals with ==, so the most straightforward way to check if an optional array contains values is:

if array?.isEmpty == false {
    print("There are objects!")
}

Other ways it can be done:

if array?.count ?? 0 > 0 {
    print("There are objects!")
}

if !(array?.isEmpty ?? true) {
    print("There are objects!")
}

if array != nil && !array!.isEmpty {
    print("There are objects!")
}

if array != nil && array!.count > 0 {
    print("There are objects!")
}

if !(array ?? []).isEmpty {
    print("There are objects!")
}

if (array ?? []).count > 0 {
    print("There are objects!")
}

if let array = array, array.count > 0 {
    print("There are objects!")
}

if let array = array, !array.isEmpty {
    print("There are objects!")
}

If you want to do something when the array is nil or is empty, you have at least 6 choices:

Option A:

if !(array?.isEmpty == false) {
    print("There are no objects")
}

Option B:

if array == nil || array!.count == 0 {
    print("There are no objects")
}

Option C:

if array == nil || array!.isEmpty {
    print("There are no objects")
}

Option D:

if (array ?? []).isEmpty {
    print("There are no objects")
}

Option E:

if array?.isEmpty ?? true {
    print("There are no objects")
}

Option F:

if (array?.count ?? 0) == 0 {
    print("There are no objects")
}

Option C exactly captures how you described it in English: "I want to do something special only when it is nil or empty." I would recommend that you use this since it is easy to understand. There is nothing wrong with this, especially since it will "short circuit" and skip the check for empty if the variable is nil.



Previous answer for Swift 2.x:

You can simply do:

if array?.count > 0 {
    print("There are objects")
} else {
    print("No objects")
}

As @Martin points out in the comments, it uses func ><T : _Comparable>(lhs: T?, rhs: T?) -> Bool which means that the compiler wraps 0 as an Int? so that the comparison can be made with the left hand side which is an Int? because of the optional chaining call.

In a similar way, you could do:

if array?.isEmpty == false {
    print("There are objects")
} else {
    print("No objects")
}

Note: You have to explicitly compare with false here for this to work.


If you want to do something when the array is nil or is empty, you have at least 7 choices:

Option A:

if !(array?.count > 0) {
    print("There are no objects")
}

Option B:

if !(array?.isEmpty == false) {
    print("There are no objects")
}

Option C:

if array == nil || array!.count == 0 {
    print("There are no objects")
}

Option D:

if array == nil || array!.isEmpty {
    print("There are no objects")
}

Option E:

if (array ?? []).isEmpty {
    print("There are no objects")
}

Option F:

if array?.isEmpty ?? true {
    print("There are no objects")
}

Option G:

if (array?.count ?? 0) == 0 {
    print("There are no objects")
}

Option D exactly captures how you described it in English: "I want to do something special only when it is nil or empty." I would recommend that you use this since it is easy to understand. There is nothing wrong with this, especially since it will "short circuit" and skip the check for empty if the variable is nil.

Community
  • 1
  • 1
vacawama
  • 150,663
  • 30
  • 266
  • 294
  • 2
    This works. Just for completeness: It uses the `func >(lhs: T?, rhs: T?) -> Bool` operator, and the right hand side `0` is wrapped into an `Int?` by the compiler. – Martin R Dec 21 '14 at 11:53
  • 1
    I just noticed that with `var opt : Int? = nil`, `opt < 0` evaluates to `true`. It is not relevant here but may be unexpected, and *may be* an argument *against* comparing optional integers. – Martin R Dec 21 '14 at 12:06
  • `nil` is less than any non-nil value `Int` you throw at it, even negative ones. It has to be at one end or the other of the wrapped type because any implementation of `<` has to be a total order, and optional could wrap any comparable type. I think nil less than some anything is intuitive, because if someone sorted a column of numbers that could be blank, I think they’d expect the blanks to come before 1. Or alternatively, a nil array variable is even more empty than an empty array :) – Airspeed Velocity Dec 21 '14 at 13:12
  • Thanks, @vacawama. The option you pointed out works. However, I went a bit more ahead and tried another particular case: only testing if the array is nil or empty, regardless of doing anything when it has elements. I will edit my question so you can take a look at it. – eze.scaruli Dec 21 '14 at 22:31
  • @vacawama Could you please tell me why you use "!(array?.isEmpty == false)" as opposed to "array?.isEmpty == true" – Rambatino Apr 02 '15 at 17:41
  • 2
    @Rambatino, `!(array?.isEmpty == false)` will be `true` if `array` is `nil`, but `array?.isEmpty == true` will be `false` if `array` is `nil` because `nil` is not equal to `true`. We want to treat a `nil` `array` as empty. – vacawama Apr 02 '15 at 18:50
  • Aha! First time to know that Swift supports ?? operator! Amazing. – Desmond Oct 24 '15 at 13:51
8

Extension Property on the Collection Protocol

*Written in Swift 3

extension Optional where Wrapped: Collection {
    var isNilOrEmpty: Bool {
        switch self {
            case .some(let collection):
                return collection.isEmpty
            case .none:
                return true
        }
    }
}

Example Use:

if array.isNilOrEmpty {
    print("The array is nil or empty")
}

 

Other Options

Other than the extension above, I find the following option most clear without force unwrapping optionals. I read this as unwrapping the optional array and if nil, substituting an empty array of the same type. Then, taking the (non-optional) result of that and if it isEmpty execute the conditional code.

Recommended

if (array ?? []).isEmpty {
    print("The array is nil or empty")
}

Though the following reads clearly, I suggest a habit of avoiding force unwrapping optionals whenever possible. Though you are guaranteed that array will never be nil when array!.isEmpty is executed in this specific case, it would be easy to edit it later and inadvertently introduce a crash. When you become comfortable force unwrapping optionals, you increase the chance that someone will make a change in the future that compiles but crashes at runtime.

Not Recommended!

if array == nil || array!.isEmpty {
    print("The array is nil or empty")
}

I find options that include array? (optional chaining) confusing such as:

Confusing?

if !(array?.isEmpty == false) {
    print("The array is nil or empty")
}

if array?.isEmpty ?? true {
    print("There are no objects")
}
Mobile Dan
  • 6,444
  • 1
  • 44
  • 44
6

Swift extension:

extension Optional where Wrapped: Collection {
    var nilIfEmpty: Optional {
        switch self {
        case .some(let collection):
            return collection.isEmpty ? nil : collection
        default:
            return nil
        }
    }

    var isNilOrEmpty: Bool {
        switch self {
        case .some(let collection):
            return collection.isEmpty
        case .none:
            return true
    }
}

Usage:

guard let array = myObject?.array.nilIfEmpty else { return }

or:

if myObject.array.isNilOrEmpty {
    // Do stuff here
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
Leo
  • 219
  • 3
  • 4
5

Option D: If the array doesn't need to be optional, because you only really care if it's empty or not, initialise it as an empty array instead of an optional:

var array = [Int]()

Now it will always exist, and you can simply check for isEmpty.

jrturton
  • 118,105
  • 32
  • 252
  • 268
  • While I agree with this, the problem is we don't know what the OP wants exactly. Depending on the API that is being used it is possible that an array either has contents, or is nil. Or is nil, empty or contains values. And each of these situations has different solutions. – Abizern Dec 21 '14 at 11:58
  • Agreed, I was just presenting an option which I've personally found useful. – jrturton Dec 21 '14 at 12:03
  • 1
    Lol, yeah. I just commented this exact thing before I read your answer. Great minds... :-) – Fogmeister Dec 21 '14 at 12:27
  • Table views is exactly the situation I'm thinking of! – jrturton Dec 21 '14 at 12:35
3

Conditional unwrapping:

if let anArray = array {
    if !anArray.isEmpty {
        //do something
    }
}

EDIT: Possible since Swift 1.2:

if let myArray = array where !myArray.isEmpty {
    // do something with non empty 'myArray'
}

EDIT: Possible since Swift 2.0:

guard let myArray = array where !myArray.isEmpty else {
    return
}
// do something with non empty 'myArray'
borchero
  • 5,562
  • 8
  • 46
  • 72
  • He still has to check if it is empty or not. This just checks whether on not the optional contains an array. – Abizern Dec 21 '14 at 11:44
  • Yeah then you can do `if array? != nil && !(array!.isEmpty) {}` – borchero Dec 21 '14 at 11:46
  • You can simply unwrap it because the line isn't executed if the array is nil and can't be unwrapped – borchero Dec 21 '14 at 11:46
  • 2
    Don't you think having to write `array?` and `array!` on the same line to be a code smell? That's what conditional unwrapping is for. – Abizern Dec 21 '14 at 11:47
  • But watch out, you have to use the logical & operator -> you have to use && instead of &. Otherwise you could get an error when the array is nil – borchero Dec 21 '14 at 11:47
  • You could also use `array?.isEmpty` but when using the logical & operator there's no need to do so – borchero Dec 21 '14 at 11:48
  • At Swift 2.0 edit; you should put else after isEmpty. @OliverBorchert – alicanbatur Mar 28 '16 at 08:37
2

The elegant built-in solution is Optional's map method. This method is often forgotten, but it does exactly what you need here; it allows you to send a message to the thing wrapped inside an Optional, safely. We end up in this case with a kind of threeway switch: we can say isEmpty to the Optional array, and get true, false, or nil (in case the array is itself nil).

var array : [Int]?
array.map {$0.isEmpty} // nil (because `array` is nil)
array = []
array.map {$0.isEmpty} // true (wrapped in an Optional)
array?.append(1)
array.map {$0.isEmpty} // false (wrapped in an Optional)
matt
  • 515,959
  • 87
  • 875
  • 1,141
0

It's better to use if to check for empty array. Why bcoz, for example if we try to find greatest integer in a list array, obviously we will compare list[0] with every integer in a list. at that time it gives us Index out of range exception.... you can try this with both IF & Guard

func findGreatestInList(list: [Int]?)-> Int? {
        if list!.count == 0 {
            print("List is empty")
            return nil
           
       }
       /*guard list!.isEmpty else {
            print("List is empty")
            return nil
        }*/
        var greatestValue = list![0]
            for number in 0...list!.count-1 {
            
              if list![number] > greatestValue {
                greatestValue = list![number]
-1

Instead of using if and else it is better way just to use guard to check for empty array without creating new variables for the same array.

guard !array.isEmpty else {
    return
}
// do something with non empty ‘array’
Phil
  • 1,200
  • 13
  • 17