【Swift】タプル と switch 文の組み合わせ

【復習】「タプル」とは

Swift にはタプルという複数の異なる型のデータをひとまとめにできる機能があります(他の言語にももちろんありますが)。

structclass を定義する必要もなく簡易的にデータをまとめることができるので便利です。

存在は知っていたけれど、あまり使うことがなかったのですが、仕事でコードレビューを受けたときに「タプルと switch を組み合わせるとまとまりが出来てスッキリするよ」と指摘されたので、復習も兼ねて、記事にまとめてみようと思います。

タプルの基本

先ずはタプルの基本から復習します。

タプルには整数・浮動小数・文字列など、型を問わずデータを格納することができます。

タプルの要素を参照するには格納した順番に、.0 .1 .2 .3 ・・・ というようにアクセスします。

let tuple = (3, 0.5, "Hello")
print(tuple)
print(tuple.0)
print(tuple.2)

出力結果
(4, 0.5, "Hello")
3
"Hello"

要素にラベル(名前)を付けられる

let tuple = (i: 3, f:0.5, s:"Hello")
print(tuple.i)
print(tuple.f)
print(tuple.s)

出力結果
3
0.5
"Hello"

struct や enum も格納できる

struct Person {
    let age: Int
    let name: String
}

enum Fruits: String {
    case banana
    case orange
    case strawberry
}

let person = Person(age: 30, name: "Taro Yamada")
let fruits: Fruits = .strawberry

let tuple = (person, fruits)
print(tuple)

出力結果
Person(age: 30, name: "Taro Yamada")
Fruits.strawberry

メソッドの戻り値にも使える

メソッドの戻り値にタプルを使うと2つ以上の情報を返すことができます。

func getTuple() -> (Int, String) {
    return (50, "Tuple")
}

let tuple = getTuple()
print(tuple)

出力結果
(50, "Tuple")

【Swift】タプル と switch 文の組み合わせ

ようやく本題ですが、switch 文にタプルを用いると、複数の条件を満たすかどうかの判定をまとめることができるのでコードの可読性がアップします

タプルの本領発揮と言っても良いかもしれません。

switch とタプルの基本形

let tuple = (1, "hello")

switch tuple {
case (0, "Hello"):
    print("case A")
case (1, "hellO"):
    print("case B")
case (1, "hello"):
    print("case C")
case (2, "hello"):
    print("case D")
}

出力結果
case C

オプショナル型も入れてみる

タプルは Int? や String? などのオプショナル型もタプルは扱えます。

let num: Int? = 4
let str: String? = "ABCD"

let tuple = (num, str)

switch tuple {
case (4?, "ABCD"?):
    print("match")
default:
    print("error")
}

上記の場合、number と str のどちらかが nil だった場合 case は成立しません(当然、4 と “ABCD” でなかった場合も同じです)。

特定の要素を無視できる

case のタプル要素を _(アンダースコア)にするとその要素は判定の対象外となります。

let n: Int = 5
let d: 2.48
let s: "STRING"
let tuple = (n, d, s)
d を判定外に
switch tuple {
case (5, _, "STRING")
    print("match")
default:
    print("not match")
}

出力結果
match
n と s を判定外に
switch tuple {
case (_, 2.48, _)
    print("match")
default:
    print("not match")
}

出力結果
match

case let でタプル変数を利用できる

case let を用いると case が成立した後、そのタプルを利用することができます。

let year: String? = nil
let month: String? = "6"
let day: String? = "18"

let date = (year, month, day)

switch date {
case let (year?, month?, day?):
    print("\(year)年\(month)月\(day)日")
case let (year?, month?, _):
    print("\(year)年\(month)月")
case let (_, month?, day?):
    print("\(month)月\(day)日")
default:
    print("not matched")
}

出力結果
6月18日

実際の開発では上記のような使い方が多いのではないでしょうか。

ちなみに、case let のタプルの後に where 句で条件を付けることもできます。

case let (year?, month?, day?) where day <= 31:

以上、タプルの復習と switch との組み合わせによる効果的な使い方を紹介しました。

洗練されたコードを書くためにも、タプルに限らず、言語の機能は積極的に使っていくことをお勧めします