Linus
Linus

Reputation: 4783

Working with optionals in Swift programming language

As far as I know the recommended way to use optionals (Int in this example) is the following:

var one:Int?

if var maybe = one {
  println(maybe)
}

Is it possible to use a shorter way to do something like the following?

var one:Int?
var two:Int?
var three:Int?

var result1 = one + two + three // error because not using !
var result2 = one! + two! + three! // error because they all are nil

Update

To be more clear about what I'm trying to do: I have the following optionals

var one:Int?
var two:Int?
var three:Int?

I don't know if either one or two or three are nil or not. If they are nil, I wan't them to be ignored in the addition. If they have a value, I wan't them to be added.

If I have to use the recommended way I know, it would look something like this: (unnested)

var result = 0

if var maybe = one {
  result += maybe
}
if var maybe = two {
  result += maybe
}
if var maybe = three {
  result += maybe
}

Is there a shorter way to do this?

Upvotes: 0

Views: 142

Answers (2)

jtbandes
jtbandes

Reputation: 118651

That's exactly the point of optionals — they may be nil or non-nil, but unwrapping them when they're nil is an error. There are two types of optionals:

T? or Optional<T>

var maybeOne: Int?
// ...

// Check if you're not sure
if let one = maybeOne {
    // maybeOne was not nil, now it's unwrapped
    println(5 + one)
}

// Explicitly unwrap if you know it's not nil
println(5 + one!)

T! or ImplicitlyUnwrappedOptional<T>

var hopefullyOne: Int!
// ...

// Check if you're not sure
if hopefullyOne {
    // hopefullyOne was not nil
    println(5 + hopefullyOne)
}

// Just use it if you know it's not nil (implicitly unwrapped)
println(5 + hopefullyOne)

If you need to check multiple optionals at once here there are a few things you might try:

if maybeOne && maybeTwo {
    println(maybeOne! + maybeTwo!)
}

if hopefullyOne && hopefullyTwo {
    println(hopefullyOne + hopefullyTwo)
}

let opts = [maybeOne, maybeTwo]
var total = 0
for opt in opts {
    if opt { total += opt! }
}

(It seems you can't use the let optional binding syntax with more than one optional at once, at least for now...)

Or for extra fun, something more generic and Swifty:

// Remove the nils from a sequence of Optionals
func sift<T, S: Sequence where S.GeneratorType.Element == Optional<T>>(xs: S) -> GeneratorOf<T> {
    var gen = xs.generate()
    return GeneratorOf<T> {
        var next: T??
        do {
            next = gen.next()
            if !next { return nil } // Stop at the end of the original sequence
        } while !(next!) // Skip to the next non-nil value
        return next!
    }
}

let opts: [Int?] = [1, 3, nil, 4, 7]
reduce(sift(opts), 0) { $0 + $1 } // 1+3+4+7 = 15

Upvotes: 3

jrturton
jrturton

Reputation: 119242

Quick note - if let is preferred for optional binding - let should always be used where possible.

Perhaps Optionals aren't a good choice for this situation. Why not make them standard Ints with a default value of 0? Then any manipulation becomes trivial and you can worry about handling None values at the point of assignment, rather than when you're working on the values?

However, if you really want to do this then a tidier option is to put the Optionals into an Array and use reduce on it:

    let sum = [one,two,three,four,five].reduce(0) {
        if ($1) {
            return $0 + $1!
        }
        return $0
    }

Upvotes: 3

Related Questions