順番つきの Set(要素が重複しない Array)な OrderedSet が Swift にやってきた
Summary
// 埼玉県 が2つある
let kantoArray = ["茨城県", "栃木県", "群馬県", "埼玉県", "埼玉県", "千葉県", "東京都", "神奈川県"]
// OrderedSet を使う
let kantoOrderedSet = OrderedSet(kantoArray)
// 要素の重複がなく順番が保証されたコレクション、OrderedSet
print(kantoOrderedSet) // [茨城県, 栃木県, 群馬県, 埼玉県, 千葉県, 東京都, 神奈川県]
関連
Set
(要素が重複しない Array
)な OrderedSet
順番つきの 関東地方を北関東と南関東に分けるとき、埼玉県をどちらに所属させるかについていろいろあるそうです。
とりあえず、両方に埼玉県を入れておくことにします。
let northKanto = ["茨城県", "栃木県", "群馬県", "埼玉県"]
let southKanto = ["埼玉県", "千葉県", "東京都", "神奈川県"]
この2つの配列から関東を作ると埼玉県
が重複してしまいます。
let kantoArray = northKanto + southKanto
print(kantoArray) // ["茨城県", "栃木県", "群馬県", "埼玉県", "埼玉県", "千葉県", "東京都", "神奈川県"]
Swift において要素が重複しない CollectionType に Set
があります。これを用いて重複を取り除いてみます。
let kantoSet = Set(kantoArray)
print(kantoSet) // ["栃木県", "埼玉県", "千葉県", "群馬県", "神奈川県", "茨城県", "東京都"]
確かに埼玉県
は1つになりましたが、Set
は要素の順番を保証しないため、実行のたびに print(kantoSet)
の出力結果が変わってしまいます。
しかし、ISO 3166-2:JP(JIS X 0401)に合わせて順番は保証したいところ…
順番つきの Set
(要素が重複しない Array
)を使いたいときは Collections にある OrderedSet
を用います。
// Swift Package Manager で Collections を導入
import Collections // または import OrderedCollections
let kantoOrderedSet = OrderedSet(kantoArray)
print(kantoOrderedSet) // [茨城県, 栃木県, 群馬県, 埼玉県, 千葉県, 東京都, 神奈川県]
NSOrderedSet ってあったよね
これまで順番つきの Set
(要素が重複しない Array
)を使いたいときは Objective-C のときからある NSOrderedSet を使うという手法がありました。しかし、NSOrderedSet
はあくまで Set
や Array
ではないため、それが欲しいときはそれぞれに合ったプロパティを用いて得る必要がありますが、これでは要素の型が Set<AnyHashable>
や [Any]
になってしまいます。
import Foundation
let kantoNSOrderedSet = NSOrderedSet(array: kantoArray)
print(type(of: kantoNSOrderedSet)) // __NSOrderedSetI
print(type(of: kantoNSOrderedSet.array)) // Array<Any>
print(type(of: kantoNSOrderedSet.set)) // Set<AnyHashable>
しかし、Collections の OrderedSet
は Hashable
に準拠した要素の型を保持しています。
print(type(of: kantoOrderedSet)) // OrderedSet<String>
print(type(of: kantoOrderedSet.elements)) // Array<String>
OrderedSet
を Set
や Array
に変換しなくても柔軟に使える
OrderedSet
はほとんどの SetAlgebra
に対応するため、そもそも Set
や Array
に変換しなくても、NSOrderedSet
のときより柔軟に取り扱うことができます。
let kantoOrderedSetSortedByUnicode: OrderedSet = ["千葉県", "埼玉県", "埼玉県", "東京都", "栃木県", "神奈川県", "群馬県", "茨城県"]
print(kantoOrderedSet == kantoOrderedSetSortedByUnicode) // false
kantoOrderedSet.sort() // kantoOrderedSet を let から var へ変更
print(kantoOrderedSet == kantoOrderedSetSortedByUnicode) // true
print(kantoOrderedSet.contains("群馬県")) // true
print(kantoOrderedSet.contains("山梨県")) // false
OrderedSet
への要素の挿入・追加は (inserted: Bool, index: Int)
のタプルが返ってくる
OrderedSet
への要素の挿入や追加で用いる insert(_:at:)
や append(_:)
メソッドは (inserted: Bool, index: Int)
のタプルが返されます。OrderedSet
にすでに挿入・追加したい要素が含まれる場合は inserted
が false
となります。
var greaterTokyoArea = OrderedSet(kantoArray)
print(greaterTokyoArea) // [茨城県, 栃木県, 群馬県, 埼玉県, 千葉県, 東京都, 神奈川県]
let result1 = greaterTokyoArea.append("山梨県")
print(result1.inserted, result1.index) // true 7
let result2 = greaterTokyoArea.append("東京都")
print(result2.inserted, result2.index) // false 5
let removedElement = greaterTokyoArea.removeLast()
print(removedElement) // 山梨県
添字によるアクセス
OrderedSet
のまま、Array
のように添字でもアクセスできます。
for index in 0..<greaterTokyoArea.count {
print("都道府県コード: \(index + 8)", greaterTokyoArea[index])
}
// 都道府県コード: 8 茨城県
// 都道府県コード: 9 栃木県
// 都道府県コード: 10 群馬県
// 都道府県コード: 11 埼玉県
// 都道府県コード: 12 千葉県
// 都道府県コード: 13 東京都
// 都道府県コード: 14 神奈川県
Discussion