MapNotNull 과 MapNotNullTo 에 대한 정리 # null 제거
오늘은 MapNotNull 과 MapNotNullTo 에 대해서 정리해 보도록 하겠습니다.
1. MapNotNull
mapNotNull은 엘리먼트에 주어진 transform 연산자를 적용하여서,
null 이 나오지 않는 값들로만 list 를 구성해서, 반환해주는 함수입니다.
이름에 map이 있지만, 반환하는 타입은 아래와 같이 List 입니다.
null 을 제거해주는 방어연산자라고도 할수 있겠습니다.
2. MapNotNull 예제1
코드를 보면서 이해해 보겠습니다.
mapNotNull연산자 안에서,
2로 나누어 나머지가 없이 딱 떨어지는 값은 2를 곱해주고,
그렇지 않은 경우는 null을 리턴하는 조건문을 넣어주었습니다.
val numbers = listOf(1, 2, 3, 4, 5, 6)
val doubledEvens = numbers
.mapNotNull { number ->
if (number % 2 == 0) {
number * 2
} else {
null
}
}
println(doubledEvens)
위 코드를 실행하면 아래와 같은 결과가 나오는데요.
mapNotNull 이 null 값을 모두 제거해 주었기 때문입니다.
[4, 8, 12]
3. MapNotNull 과 takeIf 의 사용
mapNotNull 은 takeIf 와 같이 사용하기에 좋은데요.
takeIf와 같은 operator 는 조건을 만족시키지 않으면 null을 반환해주기 때문입니다.
아래 코드에서는 2로 나누어지는 값만 take 해서 list 를 만들 수 있었습니다.
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val filteredNumbers = numbers.mapNotNull { it.takeIf { num -> num % 2 == 0 } }
println(filteredNumbers) // 결과: [2, 4, 6, 8, 10]
아래와 같이 사용해 볼 수도 있는데요.
indexOfFirst는 리스트에서 조건을 만족하지 않으면, "-1"을 반환해 주는데요.
takeIf로 -1이 아닌경우만 가져오도록 하는 것 입니다.
itemList.mapNotNull { item ->
otherItemList
.indexOfFirst { item.id == otherItem.id }
.takeIf { it != -1 }
}
4. MapNotNull 예제 2
이번에는 조금 더 복잡한 사례를 보도록 하겠습니다.
아무래도 복잡하니 완전히 이해하기 보다는, 이런식으로도 쓰일 수 있다 정도로 보면 될 것 같습니다.
원래는 중첩된 반복문을 돌려야 하는 코드인데요.
mapNotNull과 flatMap을 이용해서 아주 간결하게 작성되었습니다.
student 와 그 중 일부인 elite 학생들과의 데이터를 합쳐서 표현하기 위한 코드인데요.
students 리스트에 mapNotNull 연산자를 이용해 줍니다.
elits 리스트는 중첩되 있는 구조이기 때문에, flatMap을 이용해서 하나의 list 로 펴 주고요.
그리고 나서, student 와 eliteInfo 의 id가 같은 것을 찾아 위해서, firstOrNull 연산자를 사용해 줍니다.
그리고 나서, 매칭된 EliteStudentInfo()객체를 만들어서 반환해 줍니다.
위에서 firstOrNull 에서 매칭이 되지 않아서 null이 흘러나왔을 수 있는데요.
이 때는 그냥 mapNotNull에 의해서 list에 포함되지 않게 됩니다.
data class Student(val id: Int, val name: String, age: Int, address: String)
data class Elit(val group: String, elitInfo: ElitInfo)
data class ElitInfo(val id: Int, val class: String, val topScore: Int)
repository.getElite(id).zip(
repository.getStudent(id)
) { elits: List<Elite>, students: List<Student> ->
students.mapNotNull { student ->
elits
.flatMap { it.eliteInfo ?: emptyList() }
.firstOrNull { it.id == student.id}
?.let { matchingEliteInfo ->
EliteStudentInfo(
id = student.id
name = student.name,
age = student.age,
class = matchingEliteInfo.class,
address = student.address,
topScore = matchingEliteInfo.topScore
)
}
}
복잡한 케이스이지만,
mapNotNull과 flatMap, firstOrNull 이 없었다면,
훨신 읽기 어려운 코드가 될 수도 있었을 것 입니다.
5. MapNotNullTo
이 API는 MatNotNull 과 유사하지만,
변환된 결과값을 특정한 리스트에 저장해서 반환할 수 있도록 해 줍니다.
이렇게 하면, 마지막에 특정 리스트에 add함수를 사용할 필요도 없어지고 코드도 훨씬 보기가 좋아집니다.
data class Person(val name: String, val age: Int?)
val validAges = mutableListOf<Int>()
val people = listOf(Person("Song", 25), Person("Park", null), Person("Kim", 31))
people.mapNotNullTo(validAges) { it.age }
println(validAges)