Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift switch statement on a tuple of optional booleans

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
        }
    }
like image 516
nflacco Avatar asked Jun 27 '14 19:06

nflacco


2 Answers

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))
        }
    }
like image 102
webcpu Avatar answered Sep 24 '22 16:09

webcpu


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

like image 30
Brett Avatar answered Sep 23 '22 16:09

Brett