問題
List<Before?>
型の値を List<After?>
型に変換したい。
要素の値が null
であれば null
のまま、Before
であれば After
に変換する。
変換には、List<Before>
型の値を List<After>
型に変換する関数を
一度だけ1使用したい。
この関数は null
を受け付けないことに注意。
Before
型、After
型、および List<Before>
型の値を List<After>
型に変換する関数を、仮に次のようなものとする。
data class Before(val value: String)
data class After(val value: String)
fun List<Before>.toAfterList(): List<After> =
map { After(it.value) }
解答
次のような関数を用いることで変換できる。
fun List<Before?>.toAfterOrNullList(): List<After?> {
val beforeOrNullList = this
val afterItr: Iterator<After> = beforeOrNullList
.filterNotNull()
.toAfterList()
.iterator()
return beforeOrNullList.map { beforeOrNull ->
beforeOrNull?.run {
afterItr.next()
}
}
}
動作確認
fun main() {
listOf(Before("A"), Before("B")).also {
println("$it -> ${it.toAfterOrNullList()}")
// ^ > [Before(value=A), Before(value=B)] -> [After(value=A), After(value=B)]
}
listOf(Before("A"), null).also {
println("$it -> ${it.toAfterOrNullList()}")
// ^ > [Before(value=A), null] -> [After(value=A), null]
}
listOf(null, Before("B")).also {
println("$it -> ${it.toAfterOrNullList()}")
// ^ > [null, Before(value=B)] -> [null, After(value=B)]
}
listOf(null, null).also {
println("$it -> ${it.toAfterOrNullList()}")
// ^ > [null, null] -> [null, null]
}
}
発展
一般化して、
「List<T>
を List<R>
に変換する関数を用いて
List<T?>
を List<R?>
に変換する関数」にする。
inline fun <T, R> List<T?>.mapWithNull(
crossinline transform: (List<T>) -> List<R>
): List<R?> {
val tOrNullList = this
val rItr: Iterator<R> = tOrNullList
.filterNotNull()
.let(transform)
.iterator()
return tOrNullList.map { tOrNull ->
tOrNull?.run {
rItr.next()
}
}
}
関数名は改善の余地がありそう。
動作確認
fun main() {
listOf(Before("A"), Before("B")).also {
println("$it -> ${it.mapWithNull { it.toAfterList() }}")
// ^ > [Before(value=A), Before(value=B)] -> [After(value=A), After(value=B)]
}
listOf(Before("A"), null).also {
println("$it -> ${it.mapWithNull { it.toAfterList() }}")
// ^ > [Before(value=A), null] -> [After(value=A), null]
}
listOf(null, Before("B")).also {
println("$it -> ${it.mapWithNull { it.toAfterList() }}")
// ^ > [null, Before(value=B)] -> [null, After(value=B)]
}
listOf(null, null).also {
println("$it -> ${it.mapWithNull { it.toAfterList() }}")
// ^ > [null, null] -> [null, null]
}
}
/以上
-
この関数は呼び出しコストが大きい。 ↩