LoginSignup
200
124

More than 5 years have passed since last update.

Swift4.0でDictionaryが大幅にパワーアップした

Last updated at Posted at 2017-06-25

Swift4.0でDictionaryがかなり使いやすくなったのでざっくり紹介したいと思います。
主にProposalsの内容なので詳しくはそちらを読んでください🙇

Sequenceタイプから初期化できる

init(uniqueKeysWithValues:)

Key-ValueのペアになったSequenceタイプからDictionaryを生成できるようになりました👏

let touples = [("Apple", 1), ("Orange", 2), ("Grape", 3)]

// ["Grape": 3, "Orange": 2, "Apple": 1]
let dictionary = Dictionary(uniqueKeysWithValues: touples)

上記のようにタプルだと要素が勝手に増やせてしまいKey-Valueの構成にならないので、
Swift3で入ったzipを使うとペアの値という縛りができます。

let names = ["Apple", "Orange", "Grape"]
let count = [1, 2, 3]
let ziped = zip(names, count)

// ["Grape": 3, "Orange": 2, "Apple": 1]
let dictionary = Dictionary(uniqueKeysWithValues: ziped)

また、重複したKeyを含めた場合は実行時エラーになるので注意です⚠️

let names = ["Apple", "Apple", "Grape"]
let count = [1, 2, 3]
let ziped = zip(names, count)

// Execution was interrupted, reason: EXC_BAD_INSTRUCTION
let dictionary = Dictionary(uniqueKeysWithValues: ziped)

重複したKey(Sequenceタイプ)に対して処理できる

init(_:uniquingKeysWith:)

上記のように重複したKeyがあった場合に何かしらの処理をしてそのKeyのValueとして初期化できます。

let names = ["Apple", "Apple", "Grape"]
let count = [1, 2, 3]
let ziped = zip(names, count)

// ["Grape": 3, "Apple": 3]
let merged = Dictionary(ziped, uniquingKeysWith: +)

merging(_:uniquingKeysWith:)

初期化だけでなく既にあるDictionaryに対しても行えるmergingmerge(mutating)も追加されています🙆

let dictionary = ["Grape": 3, "Orange": 2, "Apple": 1]

// ["Grape": 6, "Orange": 2, "Apple": 4]
let merged = dictionary.merging(ziped, uniquingKeysWith: +)

FilterやMapの扱いが楽になった

filter(_:)

CollectionタイプのfilterがDictionaryのメソッドとして実装されました。
今まではタプルの配列が返ってきたのですが、ちゃんとDictionaryで返ってきます👏

let dictionary = ["Grape": 3, "Orange": 2, "Apple": 1]

// Swift3: [(key: "Grape", value: 3), (key: "Orange", value: 2)]
// Swift4: ["Grape": 3, "Orange": 2]
let filterd = dictionary.filter { $0.value > 1 }

mapValues(_:)

DictionaryのValueに何かしらの処理を加えて新たなDictionaryを生成したいことって結構あると思います。
今までは下記のようにfor-inforEachでゴリゴリにやるしかありませんでした😭

let dictionary = ["Grape": 3, "Orange": 2, "Apple": 1]

var mapped = [String: Int]()
dictionary.forEach { touple in
    mapped[touple.key] = touple.value * 2
}

// ["Grape": 6, "Orange": 4, "Apple": 2]
mapped

上記を解決するためにmapValuesというメソッドが追加されました👏
これはDictionaryのValueだけをクロージャー内で受け取り、新しいValueが返り値になっています。
なんでKeyはないんだよって思うかもしれませんが、取り出しにくくなるだけで重複させる可能性もあるのでいらないですね。

let dictionary = ["Grape": 3, "Orange": 2, "Apple": 1]

// ["Grape": 6, "Orange": 4, "Apple": 2]
let mapped = dictionary.mapValues { $0 * 2 }

ValueをNon-Optionalに取り出せるようになった

subscript(_:default:)

DictionaryにKeyが存在しないValueを取り出した場合はnilになるのでNil Coalescing Operator(??)を使っていたと思います。
Swift4ではKeyと一緒にValueにデフォルト値を指定できるようになりました👏

let dictionary = ["Grape": 3, "Orange": 2, "Apple": 1]

// 4
let potato = dictionary["Potato", default: 4]

ただ上記ような例だとそこまでありがたみもなくNil Coalescing Operatorの方がいいと思います。
しかし+=などでValueの値を取り出してすぐにいじる場合はとても重宝すると思います。今までは…

let names = ["Apple", "Orange", "Apple", "Grape"]

var dictionary = [String: Int]()
names.forEach { name in

    // Binary operator '+=' cannot be applied to operands of type 'Int?' and 'Int'
    // dictionary[name] += 1

    let value = dictionary[name] ?? 0
    dictionary[name] = value + 1
}

// ["Grape": 1, "Orange": 1, "Apple": 2]
dictionary

上記のようにValueを一度Nil Coalescing Operatorで取り出してから処理しなくてはいけませんでした。
しかしデフォルト値を使えばこんなにスッキリ書けます👏

let names = ["Apple", "Orange", "Apple", "Grape"]

var dictionary = [String: Int]()
names.forEach { dictionary[$0, default: 0] += 1 }

// ["Grape": 1, "Orange": 1, "Apple": 2]
dictionary

GroupingしたDictionaryを簡単に生成できる

init(grouping:by:)

配列などのSequenceタイプから、クロージャー内で返した値をKeyにしたDictionaryを生成できます。今までは…

let names = ["Apple", "Grape", "Apricot", "Guava"]

var initialGroup = [Character: [String]]()
names.sorted().forEach { name in

    let character = name.characters.first ?? "#"
    var names = initialGroup[character] ?? []
    names.append(name)
    initialGroup[character] = names
}

// ["G": ["Grape", "Guava"], "A": ["Apple", "Apricot"]]
initialGroup

上記のようにとても面倒だったのですが、たった数行で頭文字や文字数ごとに分けるなどができてかなり便利です👏

let names = ["Apple", "Grape", "Apricot", "Guava"]

// ["G": ["Grape", "Guava"], "A": ["Apple", "Apricot"]]
let initialGroup = Dictionary(grouping: names) { $0.first ?? "#" }

// [5: ["Apple", "Grape", "Guava"], 7: ["Apricot"]]
let countGroup = Dictionary(grouping: names) { $0.count }

容量の指定ができる

reserveCapacity(_:)

Dictionaryで格納するデータ量が決まっている場合などに容量を指定できるようになりました。
reserveCapacityを使えば指定した容量(Backing Strageを含む: 1,3,6,12..)が確保されるのでパフォーマンスアップになります👏

var dictionary = [String: Int]()
print(dictionary.capacity) // 0

dictionary.reserveCapacity(4)
print(dictionary.capacity) // 6

dictionary.reserveCapacity(7)
print(dictionary.capacity) // 12

おしまい👋

200
124
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
200
124