Swift switch statement on a tuple of optional booleans
Asked Answered
O

3

13

I'm having trouble figuring out how to use optionals inside a tuple inside a switch. The below .Some(let ...) ... syntax works as non-tuple, but inside a tuple I get expected separator stuff :(

var dict = Dictionary<String,Bool>()
dict["a"] = true

switch (dict["a"],dict["b") {
case (.Some(let a) where !a, .Some(let b) where b):
  println("false/nil, true")
case (.Some(let a) where a, .Some(let b) where !b):
  println("true, false/nil")

I want to avoid doing the following

    if let a = self.beaconList["a"] {
        if let b = self.beaconList["b"] {
            // a, b
        } else {
            // a, !b
        }
    } else {
        if let b = self.beaconList["b"] {
            // !a, b
        } else {
            // !a, !b
        }
    }
Opus answered 27/6, 2014 at 19:6 Comment(0)
M
22

There are a bunch of ways to do this, but to fix the syntax of what you are trying to do literally, you need to explicitly unbox the Optional, either by matching against .Some(false), or unwrapping it with ! (which I think is kind of weird, as you'll see)

var dict = Dictionary<String,Bool>()
dict["a"] = true
dict["c"] = false

func matchOneOrTheOtherWithOptionals(a: Bool?, b: Bool?) -> String {
    switch (a, b) {
    case (.Some(true), let b) where b == .None || !b!: // gross
        return "a was true, but b was None or false"
    case (let a, .Some(true)) where a == .None || a == .Some(false):
        return "a was None or false and b was true"
    default:
        return "They both had a value, or they were both missing a value"
    }
}

matchOneOrTheOtherWithOptionals(true, .None) // "a was true, but b was None or false"
matchOneOrTheOtherWithOptionals(true, false) // "a was true, but b was None or false"
matchOneOrTheOtherWithOptionals(.None, true) // "a was None or false and b was true"
matchOneOrTheOtherWithOptionals(false, true) // "a was None or false and b was true"

matchOneOrTheOtherWithOptionals(false, false) // "They both had a value, or they were both missing a value"
matchOneOrTheOtherWithOptionals(true, true) // "They both had a value, or they were both missing a value"
matchOneOrTheOtherWithOptionals(.None, .None) // "They both had a value, or they were both missing a value"

You could also try the following:

func noneToFalse(bool: Bool?) -> Bool {
    if let b = bool {
        return b
    } else {
        return false
    }
}

func matchOneOrTheOther(a: Bool, b: Bool) -> String {
    switch (a, b) {
    case (true, false):
        return "a is true, b was false or None"
    case (false, true):
        return "a was false/None, b was true"
    default:
        return "both were true, or both were false/None"
    }
}

matchOneOrTheOther(noneToFalse(dict["a"]), noneToFalse(dict["b"]))

Here's a gist of the Playground I used while writing this answer: https://gist.github.com/bgrace/b8928792760159ca58a1

Matter answered 27/6, 2014 at 22:56 Comment(2)
You can avoid the !b! grossness by making both the a & b args implicitly-unwrapped optionals with matchOneOrTheOtherWithOptionals(a: Bool!, b: Bool!). Then your case wheres become just where b == .none || !b: & where a == .none || !a:. This is totally safe to do here because you're explicitly checking both a & b for .none before using the wrapped boolean value.Colorant
switch (a ?? false, b ?? false) can replace nonToFalse() functionImperception
B
2

Simplify!

var dict = Dictionary<String,String>()

dict["a"] = "the letter a"

switch (dict["a"],dict["b"]) {
  case (.None, let b):
    println("false/nil, true \(b)")
  case (let a, .None):
    println("true, false/nil \(a)")
  default:
    println("don't know") 
}
Bevel answered 27/6, 2014 at 19:16 Comment(1)
OP here: I want to match on nil/false (which .None does, I think), or true. Unfortunately "let a" or "let b" also allows nil/false :(, so I need to find a different way to do this.Opus
R
1

The accepted answer is out of date. So I rewrote it in Swift 5.

    var dict = Dictionary<String,Bool>()
    dict["a"] = true
    dict["c"] = false

    func matchOneOrTheOtherWithOptionals(_ a: Bool?, _ b: Bool?) -> String {
        switch (a, b) {
        case (.some(true), .none), (.some(true), .some(false)):
            return "a was true, but b was None or false"
        case (.none, .some(true)), (.some(false), .some(true)):
            return "a was None or false and b was true"
        default:
            return "They both had a value, or they were both missing a value"
        }
    }

    let xs: [Bool?] = [true, false, .none]
    for a in xs {
        for b in xs {
            print("a = \(String(describing: a)), b=\(String(describing: b))")
            print(matchOneOrTheOtherWithOptionals(a,b))
        }
    }
Robi answered 18/9, 2020 at 18:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.