Swiftはモダンな言語の一つです。
毎年メジャーアップデートもされるので最新の言語の一つとも言えます(毎回対応するの大変ですが...)。
今回はモダンな言語によく取り入れられているmap、filter、forEachを紹介します。
他の言語でもよく使うので、覚えておいて損はないかと思います。
使い慣れるとどんな処理も、mapを使って表現できないか考えるようになります。
map
例
struct User {
let id: String
let name: String
}
let users = [
User(id: "111", name: "aaa"),
User(id: "112", name: "bbb"),
User(id: "113", name: "ccc"),
User(id: "114", name: "ddd"),
User(id: "115", name: "eee"),
User(id: "116", name: "fff"),
]
var result = [String]()
for user in users {
result.append(user.id)
}
print(result)
// ["111", "112", "113", "114", "115", "116"]
UserのidをまとめてString型の配列を作成しています。
大学の授業とかだとこんな感じでコーティングしましょうと習うかと思います。
しかし、Swiftではもっと短いコードで書くことができます。
struct User {
let id: String
let name: String
}
let users = [
User(id: "111", name: "aaa"),
User(id: "112", name: "bbb"),
User(id: "113", name: "ccc"),
User(id: "114", name: "ddd"),
User(id: "115", name: "eee"),
User(id: "116", name: "fff"),
]
let result = users.map { $0.id }
print(result)
// ["111", "112", "113", "114", "115", "116"]
mapを使うことでたった一行で書けてしまいます!
$0
がよくわからない方はこちらを確認してください。
let result = users.map { user in
user.id
}
こちらの記述方法でなんとなくわかるかと思います(for~inとそっくり)。
user in
を省略すると$0
で表現することができるのです。
結論
mapは各要素に何かしらの処理を行い、結果を配列にして返すことができる!
compactMap
例
struct User {
let id: String
let name: String
}
let users = [
User(id: "111", name: "aaa"),
User(id: "112", name: "bbb"),
User(id: "113", name: "ccc"),
User(id: "a114", name: "ddd"),
User(id: "a115", name: "eee"),
User(id: "a116", name: "fff"),
]
let stringUserIdList = users.map { $0.id }
var result = [Int]()
for stringUserId in stringUserIdList {
if let id = Int(stringUserId) {
result.append(id)
}
}
print(result)
// [111, 112, 113]
先程はmapを使って簡単にStringの配列を作成しました。
しかし、idをStringではなくInt
に変換して配列にしたい場合もあるかと思います。
今のままだと結局、ソースコードが長くなってしまいます。
このような場合にはcompactMap
を使用します。
let result = users
.compactMap { Int($0.id) }
print(result)
// [111, 112, 113]
users.map { Int($0.id) }
ではInt?の配列が返却されます。
当然、StringからIntにキャストしようとすると100%キャストできるわけではないのでInt?が返却されます。
しかしcompactMap
は、nil
なものは取り除きます。
そのため、返却される型はIntの配列になります。
結論
compactMapは各要素に何かしらの処理を行い、結果をnilのない配列にして返すことができる!
flatMap
例
let users = [
[
User(id: "101", name: "man1"),
User(id: "102", name: "man2"),
User(id: "103", name: "man3"),
User(id: "104", name: "man4"),
User(id: "105", name: "man5")
],
[
User(id: "201", name: "women1"),
User(id: "202", name: "women2"),
User(id: "203", name: "women3"),
User(id: "204", name: "women4"),
User(id: "205", name: "women5")
]
]
var result = [String]()
// genderという名前はよろしくない...
for gender in users {
for user in gender {
result.append(user.id)
}
}
print(result)
// ["101, "102", "103", "104", "105", "201", "202", "203", "204", "205"]
このように、男性女性別々になっている連想配列があったとします。
このusersから全てのidを抜き出すときはこんな感じで実装するかと思います。
当然for文を2回繰り返しますよね...
でもflatMap
を使えば楽ちんです!
let result = users
.flatMap { $0 }
.map { $0.id }
print(result)
// ["101, "102", "103", "104", "105", "201", "202", "203", "204", "205"]
flatMap
は文字の通りフラットにしてくれます。
結論
flatMapは階層の深い要素をフラットにしてくれる!
map、compactMap、flatMapのメリット
- ソースコードが短くなる
- 知識がある同士なら可読性が上がる
- 知らないメンバーがいるのであれば勉強会を開催して共有するべき
- 変数名に悩まなくて済む
- 複雑な処理ほど中間の要素を保持する変数の命名が増えるのでありがたい
参考までに...
以前のSwiftバージョンではcompactMap
が存在しませんでした。
確か、flatMap
がcompactMap
の機能も持っていたので非常にややこしくなっていたらしいです。
最新のバージョンではその問題は解消されたので頻繁に利用していきましょう!
filter
例
struct User {
/// ID
let id: String
/// 名前
let name: String
/// 性別 1:男性、2:女性、0:その他
let gender: Int
/// 身長
let height: Float
}
let users = [
User(id: "001",
name: "たんじろう",
gender: 1,
height: 165),
User(id: "102",
name: "ねずこ",
gender: 2,
height: 153.0),
User(id: "103",
name: "ぜんいつ",
gender: 1,
height: 165.5),
User(id: "104",
name: "いのすけ",
gender: 1,
height: 164.0)
]
// 男性のみを取得
var mens = [User]()
// 164cm以下のUserのみを取得
var result = [User]()
for user in users {
if user.gender == 1 {
mens.append(user)
}
if user.height <= 164.0 {
result.append(user)
}
}
print(mens)
print(result)
mapを紹介したとき同様、
取得した結果を格納する変数(配列)を用意して、for文内のif文で通過したものを変数に格納しています。
filter
を使えばもう少しかっこよく書くことができます。
// 男性のみを取得
let result = users.filter { $0.gender == 1 }
// 164cm以下の男性のみを取得
let result = users
.filter { $0.gender == 1 }
.filter { $0.height <= 164.0 }
積極的に使っていきましょう!!
forEach
使う目的はfor~in
と同様です。
省略して書きたい場合にforEach
を使うことが多いです。
// for~in
for user in users {
print(user)
}
// forEach
users.forEach { print($0) }