Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

Swift 4の新機能――拡張されたDictionary、JSON解析に役立つCodableプロトコル

Swift 4で最初に知っておきたい3つのポイント 第2回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2017/12/08 14:00

 本連載では、Swiftの基本的なプログラムができる読者を対象に、「Xcode 9」と「Swift 4」を使ってアプリを開発する際の基本的な内容を、「Swiftでアプリ開発を行う上での基本的な記述の仕方や文法など」「アプリの画面上での具体的な処理など」「外部ライブラリを利用してアプリに機能を実装する方法」の3回に分けて解説します。

目次

 

はじめに

 Swiftは2014年の公開以来、3.1までバージョンが更新されてきました。バージョンが上がると同時に大きな仕様が相次ぎ、互換性がなかったため次のバージョンアップへの不安もありました。

 ですがSwift 3から4へのバージョンアップで、メソッドや関数の定義や命名規則が統一され、今後は極端な仕様の変更などもなくなると考えられています。

 連載第2回では、アプリ内でよく利用されるDictionaryの扱いとJSONの解析に関し、Swift 4で追加された便利な機能を解説します。各項目で説明する内容についてのSwiftのバージョンは【】内に記します。

 本連載は趣旨の通り、アプリ開発の最初に知っておくべき基本的な事柄の説明をメインテーマとしています。内容によっては、Swift 4以前のバージョンに触れることやSwift自体の言語仕様などには説明が及ばないこともご了承ください。本連載以上の各種情報は末尾の参考文献等を参照してください。

対象読者

 本記事は、次の方を対象にしています。

  • Swiftでの基本的なプログラムができる方
  • Xcodeを使える方

Dictionaryの拡張された機能

 Swift 3以前でも、Arrayに対してはmap/filter/reduce等の便利なメソッドが実装されていました。その一方で、Dictionaryに関してはそういった機能はほぼ存在していませんでした。Swift 4からはDictionaryについても同様に、便利に使える機能が実装されました。ここではその中でもよく利用されるものについて例を挙げます。

Dictionary定義の簡略化【Swift 4】

 Dictinonary型の変数を宣言する際に、配列の定義と同様に[]リテラルを利用できるようになりました。名前とスコアのペアを保持するDictionary型の変数は次の通りに宣言できます。

リスト1 DictionaryViewController.swift
// Dictionary型の変数を宣言
let scoreDic: [String: Int] = ["Muku": 10, "Tora": 20, "Tama": 30, "Tom": 40]
        
// 以下の宣言と同じ意味です
//let scoreDic: Dictionary<String, Int> = ["Muku": 10, "Tora": 20, "Tama": 30, "Tom":40]

 Swift 3以前の書式と同様に、キーと値の順序は変わりません。

条件に合致する要素を取得する【Swift 4】

 条件に合致する要素を取得するfilter(_:)メソッドが実装されました。filterメソッド(_:)の書式は次の通りです。

書式1 filterメソッド
Dictionaryオブジェクト.filter(クロージャ)

 クロージャとは名前のない関数のことです。filter(_:)メソッドのクロージャ内では、$0でDictionaryの要素が得られ、keyプロパティで要素のキー、valueプロパティで要素の値を参照できます。

リスト2 DictionaryViewController.swift
// スコアが15以上の要素を取得
let scoreOver15Dic = scoreDic.filter({ $0.value > 15})
print(scoreOver15Dic)  //    結果:["Tom": 40, "Tora": 20, "Tama": 30]
        
// キーの文字数が3より大きい要素を取得
let char4Dic = scoreDic.filter({$0.key.count > 3})
print(char4Dic)  //    結果:["Muku": 10, "Tora": 20, "Tama": 30]

 filter(_:)メソッドを利用することで、Dictionaryオブジェクト内の要素を一定の条件で容易に抽出できます。

要素の値に対して処理を行う【Swift 4】

 各要素に対して処理を行うmapValue(_:)メソッドが実装されました。mapValues(_:)メソッドの書式は次の通りです。

書式2 mapValues(_:)メソッド
Dictionaryオブジェクト.mapValues(クロージャ)

 mapValues(_:)メソッドを利用して、スコアの値を3倍にする処理は次のように書けます。

リスト3 DictionaryViewController.swift
// スコアを3倍に
let scoreX2Dic = scoreDic.mapValues({ $0 * 3})
print(scoreX2Dic)    //  結果:["Muku": 30, "Tom": 120, "Tora": 60, "Tama": 90]

 mapValues(_:)メソッドは、その名前の通り辞書の値に対する処理です。$0で得られる値はDicitionaryの要素の値です。キーは含まれません。$0の意味がfilterメソッドとは違う点に気をつけてください。

配列のグループ化【Swift 4】

 配列をある規則でグループ化した後に、その結果をDictionaryで得るinit(grouping:by:)メソッドという初期化処理が追加されました。init(grouping:by:)メソッドの書式は次の通りです。

書式3 init(grouping:by:)メソッド
let Dictionaryオブジェクト = Dictionary(grouping: Arrayオブジェクト, by: クロージャ)

 クロージャでは$0でArrayオブジェクトの要素が得られます。具体的な利用例は次の通りです。

リスト4 DictionaryViewController.swift
// 要素の最初の1文字でグループ化
let people = ["あべ", "あさの", "あそう", "かとう", "かんだ", "さとう", "さやま", "たなか"]
let groupedPeopleDic = Dictionary(grouping: people, by: { $0.characters.first! })
print(groupedPeopleDic)
// 結果:["か": ["かとう", "かんだ"], "さ": ["さとう", "さやま"], "あ": ["あべ", "あさの", "あそう"], "た": ["たなか"]]

// 1~10の数字を偶数と奇数で分ける
let numbers = 1 ... 20
let groupedNum = Dictionary(grouping: numbers, by:  {  $0 % 2 == 0 })
print(groupedNum)
// 結果:[false: [1, 3, 5, 7, 9], true: [2, 4, 6, 8, 10]]

 任意の条件で配列をグループ化する機能は、ゲームやSNSでよく利用されるものです。もう少し実用的なサンプルを作成してみます。

 リスト5のような動物の名前と種類を持つ動物を表すAnimal構造体があると仮定します。動物の種類についてはenumで指定できる値を限定しています。

リスト5 DictionaryViewController.swift
struct Animal {
    var name : String           // 名前
    var type : AnimalType    // 種類 
    enum AnimalType {
        case dog, cat, mouse, panda
    }
}

 この動物オブジェクトが複数混在する場合、その中からパンダの種類だけ取り出す処理は、グループ化を使って次のように行うことができます。

リスト6 DictionaryViewController.swift
// 複数の動物オブジェクトを配列にする
let animals = [
        Animal(name:"レオ", type:.dog),  Animal(name:"むく", type:.cat),  Animal(name:"悠悠", type:.panda), 
        Animal(name:"ジェリー", type:.mouse),  Animal(name:"たま", type:.cat),   Animal(name:"トム", type:.cat),
        Animal(name:"ちゅー太", type:.mouse),  Animal(name:"ちび", type:.cat),
        Animal(name:"香香", type:.panda),  Animal(name:"とら", type:.cat)
]

// typeでグループ化する
let groupin = Dictionary(grouping: animals, by: { $0.type})
// パンダのグループ
let pandaGroup = groupin[.panda]
// Optional([Sample.Animal(name: "悠悠", type: Sample.Animal.AnimalType.panda), Sample.Animal(name: "香香", type: Sample.Animal.AnimalType.panda)])

 並列関係にある同じ型のオブジェクトは、一度配列にすれば属性で容易に分類することが可能です。for文やif文を使った長い処理を行う必要がなく、コードをシンプルに記述することができるようになりました。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • WINGSプロジェクト 片渕 彼富(カタフチ カノトミ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2017年5月時点での登録メンバは52名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂き...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

バックナンバー

連載:Swift 4で最初に知っておきたい3つのポイント
All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5