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

Swift4.0でDictionaryがかなり使いやすくなったのでざっくり紹介したいと思います。
主に[Proposals](https://github.com/apple/swift-evolution/blob/master/proposals/0165-dict.md)の内容なので詳しくはそちらを読んでください🙇

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

## `init(uniqueKeysWithValues:)`

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

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

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

上記のようにタプルだと要素が勝手に増やせてしまいKey-Valueの構成にならないので、
Swift3で入った[`zip`](https://developer.apple.com/documentation/swift/1541125-zip)を使うとペアの値という縛りができます。

```swift
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を含めた場合は実行時エラーになるので注意です⚠️

```swift
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として初期化できます。

```swift
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に対しても行える`merging`と`merge`(mutating)も追加されています🙆

```swift
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で返ってきます👏

```swift
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-in`や`forEach`でゴリゴリにやるしかありませんでした😭

```swift
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はないんだよって思うかもしれませんが、取り出しにくくなるだけで重複させる可能性もあるのでいらないですね。

```swift
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にデフォルト値を指定できるようになりました👏

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

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

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

```swift
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*で取り出してから処理しなくてはいけませんでした。
しかしデフォルト値を使えばこんなにスッキリ書けます👏

```swift
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を生成できます。今までは…

```swift
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
```

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

```swift
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..`)が確保されるのでパフォーマンスアップになります👏

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

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

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

----

おしまい👋