Android 개발/Kotlin

MapNotNull 과 MapNotNullTo 에 대한 정리 # null 제거

Developer88 2023. 5. 24. 18:39
반응형

오늘은 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)

 

728x90