Swiftでよく使う高階関数

はじめに

本記事はSwiftでよく使う高階関数をまとめた記事です。
関数自体を引数や戻り値として使える関数を高階関数といいます。Swiftではコレクション操作の際に便利な高階関数が用意されいるのでご紹介します。

実行環境

この記事は以下の動作環境で動作確認しています。

  • Xcode 11.5
  • Swift 5.2.4

準備

実際のプロジェクトではAPIのレスポンスや画面の持つデータなど、structとその配列で使用されることが多いです。
そのため今回は、QiitaのAPIのレスポンスを使って実際に高階関数を使用してみようと思います。
以下のコードで記事を20件取得します。

import Foundation
import UIKit

struct Article: Codable {
    struct Tags: Codable {
        let name: String
    }
    struct User: Codable {
        let followeesCount: Int
        let followersCount: Int
        let githubLoginName: String?
        let id: String
        let itemsCount: Int
        let name: String
    }
    
    let coediting: Bool
    let commentsCount: Int
    let createdAt: String
    let id: String
    let likes_count: Int?
    let reactionsCount: Int
    let tags: [Tags]
    let title: String
    let updatedAt: String
    let url: String
    let user: User
}


let urlStr = "https://qiita.com/api/v2/items?page=1"
var urlRequest = URLRequest(url: URL(string: urlStr)!)
urlRequest.httpMethod = "GET"


let task = URLSession.shared.dataTask(with: urlRequest, completionHandler: { data, response, error in
    
    guard let data = data else { return }
    
    var articles: [Article] = []
    do {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        articles = try decoder.decode([Article].self, from: data)
    } catch let e {
        print("JSON Decode Error :\(e)")
    }

    // ここで高階関数を使っていきます。
})

task.resume()

高階関数の例

forEach

forEachは配列の操作後に返り値が必要ない時に使用します。

サンプル

以下は記事のタイトルの数だけラベルを生成してtextをセットする処理です。
値を何かにセットして、返り値は使用しない場面で使います。

articles.forEach {
    let label = UILabel()
    label.text = $0.title
}

map

mapは配列の操作後に返り値が必要な場合に使用します。

サンプル

配列の要素から特定の値を取り出したり、配列の要素を元に別のデータに変換してまた配列を作りたい時などに利用します。

// userだけの配列を作る
let users = articles.map { $0.user }

// IDとタイトルの辞書の配列を作成する。
let artices = articles.map { article -> [String: String] in
    return [article.id: article.title]
}

compactMap

compactMapもmapと同様に返り値が必要な際に利用します。
mapとの違いは返り値がオプショナルを含むかどうかです。
mapの場合はオプショナルな値はそのままオプショナルとして返却されますが、compactMapを使用するとnilを排除した非オプショナルな値となります。

サンプル

変換した値がオプショナルとなるが、nilの場合は排除したい場合などによく使います。
日付の変換やURLへの変換が思いつきます。

// 日付を変換して返す
let createdAtList = articles.compactMap { stringFromDate(string: $0.createdAt) }
// 文字列のurlをURLに変換して返す。
let urlList = articles.compactMap { URL(string: $0.url) }

filter

filterは条件に当てはまる要素のみ抽出したい時に使います。
ブロックでBool値をreturnし、その値がtrueの時は取り出す要素に含めます。

サンプル

// コメントを持っている記事のみ抽出する
let hasCommentArtices = articles.filter { $0.commentsCount > 1 }

filterで取得した結果は配列になりますが、一つだけ抽出したい場合はfirstを使います。
filterした結果にfirstを使っても良いですが、first(where: )を使用すると一回で済むので楽です。

サンプル

let nextDaysFirstArtice = articles.first(where: {
                
    guard let convertedCreatedAt = stringFromDate(string: $0.createdAt) else {
        return false
    }
        
    return Date() > convertedCreatedAt
})

おわりに

Swiftでよく使う高階関数を紹介しました。
他にもたくさん種類がありますので配列の操作をする時に是非使ってみてください。