【Swift】集合の使い方。インデックス番号や重複が無いデータの集まり。(Swift 2.1、XCode 7.2)
集合の特徴
集合(Set)は、配列と同じように複数のデータを1つの変数で管理できる入れ物である。配列と異なる点は「インデックス番号がない」、「値を重複することができない」、「集合演算を使える」の3点である。
(配列については次の記事を参照。⇒「記事」)
配列はインデックス番号「0」から順番に番号が振られた箱の中にデータを格納していき、同じ値のデータが重複しても問題ない。
一方、集合にはインデックス番号が無く、格納されている順番にも意味は無い。そして、格納するデータは重複できない。
さらに、重複しないデータの集合であることを活用して集合演算のメソッドが用意されている。情報処理試験でよく出題される和集合、差集合などの考え方だ。集合演算を使うために集合を使うプログラマーも多い。
配列は以下のコードのように[値,値,値, …]で作る。重複している値は1つにまとめられる。「:Set」を省略すると配列になってしまうので注意が必要だ。
1 2 3 4 5 6 7 8 9 10 11 |
//集合を作る var test:Set = ["山田","佐藤","山田",12] print(test) //実行結果 //[佐藤, 山田, 12] //このように書くと配列になってしまうので「Set」は省略できない。 var test2 = ["山田","佐藤","武田"] |
以下のコードのように格納する型を指定して集合を作れば、格納するデータを制限することができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//集合を作る var test:Set<String> = ["山田","佐藤","武田"] print(test) //実行結果 //["佐藤", "武田", "山田"] //指定した型以外のデータを格納しようとするとエラーが発生する var test2:Set<String> = ["山田","佐藤",12] print(test2) //実行結果 //error: cannot convert value of type 'Int' to expected element type 'String' |
空の集合を作るには以下のコードのようにする。型を指定しない空の集合を1行で定義する方法は分かっていない。2行で書くなら、格納データをつけて定義したあとに[]を代入することで空の集合にすることができる。1行で定義する方法を発見したら後日記述する。
1 2 3 4 5 6 7 8 9 10 11 12 |
//型を指定して空の集合を作る var test:Set<String> = [] var test2 = Set<String>() //別の書き方 //型を指定しない空の集合を作る var test3:Set = ["山田", 11] test3 = [] print(test3) //実行結果 //[] |
集合の要素を取得する
集合の要素にはインデックス番号がついていないので、番号を使って要素を取得することはできない。firstプロパティを使えば先頭のデータを取得することができるが、集合の格納順に意味は無いので使いどころが難しい。よって、集合は1個のデータをピンポイントで取得するといった使い方には適していない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * 集合の要素を取得する */ //インデックス番号で取得することはできない。 var test:Set<String> = ["山田","佐藤","武田"] print(test[1]) //実行結果 //error: cannot subscript a value of type 'Set<String>' with an index of type 'Int' //firstプロパティで要素を1つ取り出すことはできる。 print(test.first!) //実行結果 //佐藤 |
集合のすべての要素を1つずつ取得するには以下のコードのようfor-in文を用いる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* * 集合の要素を1つずつ取得する */ //要素数ぶんループ var test:Set = ["山田","佐藤","武田"] for testdata in test { print(testdata) } //実行結果 //武田 //佐藤 //山田 |
その他、集合の情報を知るプロパティには以下のようなものがある。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
var test:Set = ["山田","佐藤","山田","武田",12] test.removeFirst() print(test) //実行結果 [武田, 12, 山田] //集合が空かチェックする print(test.isEmpty) // 実行結果 false //集合の要素数を取得 print(test.count) // 実行結果 3 //集合の中に「武田」があるかをチェックする print(test.contains("武田")) //実行結果 true //集合の情報を表示 print(test.description) print(test.debugDescription) //実行結果 //[武田, 12, 山田] //Set([武田, 12, 山田]) |
集合を操作する
集合に要素を追加するには以下のコードのようにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * 【メソッド】insert * 【説明】集合に要素を追加 */ //集合に"木下"を追加 var test:Set = ["山田","佐藤","武田"] test.insert("木下") print(test) //実行結果 //["木下", "武田", "佐藤", "山田"] //決められた型以外のデータを格納しようとするとエラーになる test.insert(77) //実行結果 //error: cannot convert value of type 'Int' to expected argument type 'String' |
集合の要素を削除するには以下のコードのようにする。removeメソッドの戻り値に削除したデータが設定されてくるので、戻り値を確認してデータが削除されたかを知ることができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* * 【メソッド】remove、removeAll * 【説明】集合の要素を削除 */ //集合から"山田"を削除する var test:Set = ["山田","佐藤","武田","木下"] var old = test.remove("山田") if(old != nil) { print("データを削除しました。 \(old!)") } print(test) //実行結果 //データを削除しました。山田 //["木下", "武田", "佐藤"] //集合の要素をすべて削除する var test2:Set = ["山田","佐藤","武田","木下"] test2.removeAll() print(test2) //実行結果 //[] |
集合の要素から要素を1つ取り出すにはpopFirstメソッドを使う。取り出した要素は集合から削除される。1回使用すれば不要になるデータを処理するときに便利なメソッドだ。集合が空の場合はnilが戻る。
removeFirstメソッドでも同じようなことができるが、removeメソッドは集合が空の場合はエラーが発生してしまう。なので、メソッドを呼び出す前にisEmptyメソッドなどを使い、集合が空かどうかをチェックする必要がある。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/* * 【メソッド】popFirst、removeFirst * 【説明】集合の要素を取り出す。 */ //集合から要素を1つ取り出して削除する var test:Set = ["山田","佐藤","武田","木下"] var data = test.popFirst() if(data != nil) { print("ポップしたデータは\(data!)") } print(test) //実行結果 //ポップしたデータは木下 //["武田", "佐藤", "山田"] //集合から要素を1つ削除する var test2:Set = ["山田","佐藤","武田","木下"] var data2 = test2.removeFirst() print("削除したデータは\(data2)") print(test2) //実行結果 //削除したデータは木下 //["武田", "佐藤", "山田"] |
集合演算
集合を使う大きな利点は集合演算にある。例えば、下図のA、Bの集合があったとする。このとき、AとBの片方だけに存在するデータを抽出したい。AとBの両方に存在すデータを抽出したい。このような願いを叶えてくれるのが集合演算である。
和集合は「AとBの全てのデータを抽出したい」ときに使える集合演算である。A、Bに格納されている全てのデータを重複を取り除いて取得することができる。
具体的には以下のコードのようになる。unionInPlaceメソッドを使えば、和集合の結果を元の集合に代入することができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* * 【メソッド】union、unionInPlace * 【説明】2つの集合の和集合を取得する */ var A:Set = ["佐藤","田中","緒方","鈴木","中島"] var B:Set = ["大野","田中","緒方","木田"] //AとBの和集合を取得する(A、Bは変化しない) print(A.union(B)) //実行結果 //["緒方", "鈴木", "田中", "中島", "大野", "佐藤", "木田"] //AとBの和集合をAに代入する(Aが変化する) A.unionInPlace(B) print(A) //実行結果 //["緒方", "鈴木", "田中", "中島", "大野", "佐藤", "木田"] |
差集合は「Aだけにあるデータを抽出したい。」または「Bだけにあるデータを抽出したい。」ときに使える集合演算である。AからBを引く、または、BからAを引くイメージである。
具体的には以下のコードのようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* * 【メソッド】subtract、subtractInPlace * 【説明】2つの集合の差集合を取得する。 */ var A:Set = ["佐藤","田中","緒方","鈴木","中島"] var B:Set = ["大野","田中","緒方","木田"] //差集合(AからBを引く)を取得する。 print(A.subtract(B)) //実行結果 //["鈴木", "佐藤", "中島"] //差集合(AからBを引く)の結果をAに代入する。 A.subtractInPlace(B) print(A) //実行結果 //["鈴木", "佐藤", "中島"] |
積集合は「AとBのどちらにも存在するデータを抽出したい」ときに使える集合演算である。
具体的には以下のコードのようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* * 【メソッド】intersect、intersectInPlace * 【説明】2つの集合の積集合を取得する。 */ var A:Set = ["佐藤","田中","緒方","鈴木","中島"] var B:Set = ["大野","田中","緒方","木田"] //積集合を取得する。 print(A.intersect(B)) //実行結果 //["緒方", "田中"] //積集合の結果をAに代入する。 A.intersectInPlace(B) print(A) //実行結果 //["緒方", "田中"] |
対称差集合は「AとBの片方だけにあるデータを抽出したい」ときに使える集合演算である。
具体的は以下のコードのようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * 【メソッド】exclusiveOr、exclusiveOrInPlace * 【説明】2つの集合の対称差集合を取得する。 */ var A:Set = ["佐藤","田中","緒方","鈴木","中島"] var B:Set = ["大野","田中","緒方","木田"] //対称差集合を取得する。 print(A.exclusiveOr(B)) //実行結果 //["大野", "鈴木", "佐藤", "中島", "木田"] //対称差集合の結果をAに代入する。 A.exclusiveOrInPlace(B) print(A) //実行結果 //["大野", "鈴木", "佐藤", "中島", "木田"] |
上記4つの集合演算を使えば、集合を組み合わせて複雑なデータ抽出作業を簡単に行えるようになる。
ちなみに、配列は「+演算子」を使って配列同士を結合することができるが、集合はできない。なので、集合で結合が必要なときは必ずunionを使うようにしよう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/* * 「+演算子」を使って集合を結合することはできない */ //配列同士は「+演算子」を使って結合できる。 var hairetsu1:Array<String> = ["山田","佐藤","武田"] var hairetsu2:Array<String> = ["あ","い","う"] var hairetsu3 = hairetsu1 + hairetsu2 print(hairetsu3) //実行結果 //["山田", "佐藤", "武田", "あ", "い", "う"] //集合同士は「+演算子」を使って結合できない var shuugou1:Set<String> = ["A","B","C"] var shuugou2:Set<String> = ["焼肉","寿司","野菜"] var shuugou3 = shuugou1 + shuugou2 //実行結果 //error: binary operator '+' cannot be applied to two 'Set<String>' operands //配列に集合の要素を追加することはできる var hairetsu4 = hairetsu1 + shuugou1 print(hairetsu4) //実行結果 //["山田", "佐藤", "武田", "C", "B", "A"] |