@negi0205さん
@sakuraihiromi さん
からアドバイスをいただき少々記事を更新いたしました!
先月参加した「iOSDC 2018 Reject Conference」の勉強会で
compactMapValuesについて知りました!
少しだけその特徴を紹介すると共に、
・filter
・map
・flatMap
・compactMap
・mapValues
について調べたことをお話しいたします!
(まだ知識が浅いため、他の方の記事を参考にしつつ記述していきます💦)
記事に誤り等ございましたら教えていただけると非常に嬉しいです!
ちなみにDictionary.compactMapValuesはSwift5で導入されるそうです!
まずは私自身、以前まで違いについて理解できていなかったfilterとmapについてお話しします。
##filterとmapについて
filter: 条件に合う要素の配列を作り出す
map: 配列の要素に処理を施し、その処理を施した配列を作り出す
<<使用例>>
・filter
let array = [1, 2, 3, 4, 5]
let newArray = array.filter { $0 == 2 }
// newArray = [2]
以前、天気予報アプリを作成し、検索した市が保存されているかどうかを確かめる際にfilterを用いました。
以下はその実装内容です。
// 市が登録されてるかどうか判定するメソッド
/// - Parameter id: 市のid
/// - Returns: 登録されていたらTrue
func isRegistedCity(id: String)-> Bool {
if let city = cities.first(where: { $0.id == id }) {
print(city)
return true
} else {
return false
}
}
・map
let array = [1, 2, 3, 4, 5]
let newArray = array.map { $0 * 2 }
// newArray = [2, 4, 6, 8, 10]
また研修でQiitaアプリを作成した際、mapを用いてtagsの配列からnameの要素の配列を生成しました。
// tagsにはnameとversionの要素が入っている
let names = item.tags.map { $0.name }
上記のように、filterは配列の中から条件と合うものがあるかどうかふるいをかけるイメージで、
mapは配列の中から欲しい要素の配列を新しく生成するために用います。
##flatMapについて
flatMap: mapのような動きをし、2重の配列を1重に直す
swift4.1から下記のflatMap(_:)メソッドは非推奨になりました。
func flatMap(:(Element) -> U?) -> [U]
func flatMap(: (Element) -> String?) -> [String]
<<使用例>>
・map
let array = [[1, 2], [3, 4, 5]]
let newArray = array.map { $0 + [1] }
// newArrat = [[1, 2, 1], [3, 4, 5, 1]]
・flatMap
let array = [[1, 2], [3, 4, 5]]
let newArray = array.flatMap { $0 + [1] }
// newArrat = [1, 2, 1, 3, 4, 5, 1]
また、以下のようなこともできます!
let number = [1, 2, 3]
let a = number.map { [Int](repeating: $0, count: $0) }
// a = [[1], [2, 2], [3, 3, 3]]
let number = [1, 2, 3]
let b = number.flatMap { [Int](repeating: $0, count: $0) }
// b = [1, 2, 2, 3, 3, 3]
##compactMapについて
compactMap: flatMapがリネームされたもの
今回、リネームされた経緯についてはここでは詳しく語りません。
ちなみに私は下記の記事を読んで理解しました。
https://qiita.com/ysn551/items/7cb4fbb67a0640774d2c
下記のように、Int型の配列だけを取り出す処理やnilを取り除いて配列を生成する処理を書くと
「compactMapに入れ替えなさい」というエラーが表示されます。
compactMapに書き換えると、エラーが消えます。
let nums1 = ["1", "2", "three", "4", "5"].compactMap { Int($0) }
// nums1 = [1, 2, 4, 5]
let nums2 = [1, 2, nil, 4, 5].compactMap { $0 }
// nums2 = [1, 2, 4, 5]
flatMapやcompactMapでは、上記の通りnilを取り除くことができ便利です。
##mapValuesについて
mapValues: DictionaryのValueだけをクロージャー内で受け取り、新しいValueを返り値としてDictionaryを新たに生成する
let dictionary: [String: Int] = ["Apple": 3, "Orange": 2, "Strawberry": 4]
let mapped = dictionary.mapValues { $0 * 2 }
// mapped = ["Apple": 6, "Orange": 4, "Strawberry": 8]
##compactMapValuesについて
今までの話を踏まえた上で、ようやくご紹介したかったcompactMapValuesについてお話しいたします。
冒頭で述べた通り、swift5から導入されます。
ちなみにswift5は今年は来ないことが正式に発表され、2019年の前半になるそうです。。。
それではどのような機能があるのか見てみましょう!
下記はDictionaryのValueに一部nilが入っている場合の処理です。
let dictionary: [String: String?] = ["a": "1", "b": nil, "c": "3"]
let d = dictionary.filter { $0.value != nil }.mapValues { $0! }
// d = ["a": "1", "c": "3"]
filterでnil以外のものをふるいにかけ、その後アンラップしてDictionaryを生成しています。
次に、compactMapValuesを用いて同じ処理を行うと以下のように書くことが出来ます。
let d: [String: String?] = ["a": "1", "b": nil, "c": "3"]
let r4 = d.compactMapValues({$0})
// r4 == ["a": "1", "c": "3"]
また、任意の型で変換を行う処理も少ないコードで書くことが出来ます。
let d: [String: String] = ["a": "1", "b": "2", "c": "three"]
let r5 = d.compactMapValues(Int.init)
// r5 == ["a": 1, "b": 2]
2019年が待ち遠しいですね🌸
##参考記事
https://github.com/apple/swift-evolution/blob/master/proposals/0218-introduce-compact-map-values.md
https://qiita.com/shimesaba/items/1a89cb5600454f91cc67
https://qiita.com/hanawat/items/341a6f7843f4e19780b5
https://qiita.com/motokiee/items/cf83b22cb34921580a52
https://qiita.com/ysn551/items/7cb4fbb67a0640774d2c