24-03-21 Kotlin 컬렉션 필터링하기 filter(), filterIndexed() ...
오늘도 CS공부와 코틀린 문법에 대해서 공부해보았다. 코틀린에서 컬렉션을 필터링 하는 법에 대해 잘 모르고 있어 그에 대해 공부해보았고 정리해보았다.
1. Kotlin에서 컬렉션을 필터링하기
여러 연습 문제를 풀어보면서 다른 사람들의 답안을 보니 컬렉션을 필터링하는 filter()같은 함수들을 잘 활용하는 것을 볼 수 있었다. 꼭 숙지하고있어야 겠다는 생각이 들어 코틀린의 공식문서와 구글링을 통해 알아본 내용들을 정리해보았다.
https://kotlinlang.org/docs/collection-filtering.html#filter-by-predicate
Filtering collections | Kotlin
kotlinlang.org
1-1. filter()
코틀린에서 컬렉션을 필터링하는 가장 기본적인 함수는 filter()이다. 조건자를 입력값으로 받아 호출되면 그에 맞는 컬렉션의 요소들을 포함하는 컬렉션을 반환한다. 리스트(List)와 셋(Set)에 대해서는 결과로 리스트가 반환되고, 맵(Map)에 대해서는 결과로 맵(Map)이 반환된다.
val numbers = listOf("one", "two", "three", "four")
val longerThan3 = numbers.filter { it.length > 3 }
// 리스트에서 문자열의 길이가 3이 넘는 것만 리스트로 반환한다
println(longerThan3) // [three, four]
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") || value > 10}
// 맵에서 key가 1로 끝나거나, 값이 10이 넘는 요소만 맵으로 반환한다
println(filteredMap) //{key1=1, key11=11}
1-2. filterIndexed()
filter()를 통해서는 컬렉션의 값에 대해서만 필터링 할 수 있다. filterIndexed()를 이용하면 요소의 인덱스에 대해서 필터링을 할 수 있다. filterIndexed()는 인덱스와 요소 두 가지 입력값을 가지는 조건자와 함께 호출되어야한다.
val numbers = listOf("one", "two", "three", "four")
val filteredIdx = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5) }
// 인덱스가 0이 아니거나 길이가 5보다 작은 요소만 담은 리스트를 반환한다
println(filteredIdx) // [two, four]
val numbers2 = listOf(0,1,3,4,6,5)
val filteredNumbers = numbers2.filterIndexed { index, i-> index == i }
// 인덱스와 요소의 값이 같은 요소만을 담은 리스트를 반환한다
println(filteredNumbers) // [0, 1, 5]
1-3. filterNot()
filterNot()은 조건자가 false가 될 때의 요소들을 모아 리스트로 반환한다.
val numbers = listOf("one", "two", "three", "four")
val filteredNot = numbers.filterNot { it.length <= 3 }
// 요소의 길이가 3 이하가 아닌 요소만을 가진 리스트를 반환한다
println(filteredNot) // [three, four]
1-4. filterNotNull()
filterNotNull() 컬렉션에서 null인 요소를 걸러낸다. 그리고 모든 요소는 non-nullable(null을 허용하지 않는)이 된다
val numbers = listOf(null, "one", "two", null)
println(numbers.filterNotNull()) // [one, two]
numbers.filterNotNull().forEach {
println(it.length) // null을 허용하는 문자열의 경우 length 속성은 사용이 불가능하다
}
// 출력값
// 3
// 3
1-5. partition()
partition()은 조건자를 기준으로 컬렉션을 필터링하고 일치하지 않는 요소 역시 별도의 리스트로 반환한다. 따라서 반환되는 값은 두 개의 리스트를 가진 Pair가 된다. 첫 번째 리스트에는 조건자와 일치되는 값들이, 두 번째 리스트에는 조건자와 일치되지 않는 값들이 들어있게 된다.
(Pair에 대해서는 이곳에 정리해두었다 https://moomoo11.tistory.com/13#2.%20Pair%EC%99%80%20zip()-1)
val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }
// 문자열의 길이가 3보다 큰 값들은 match에 나머지는 rest에 담겨 리스트로 출력된다
println(match) // [three, four]
println(rest) // [one, two]
val array = intArrayOf(1, 2, 3, 4, 5)
val (even, odd) = array.partition { it % 2 == 0 }
// 조건자에 따라 짝수는 even에 홀수는 odd에 리스트로 담기게 된다
println(even) // [2, 4]
println(odd) // [1, 3, 5]
1-6. any(), none(), all()
위 세 개의 함수는 컬렉션의 요소에 대해 조건자를 간단히 테스트하는 함수이다.
any()는 컬렉션의 요소 단 하나라도 조건자를 만족하면 true를 반환한다.
none()은 컬렉션의 요소 모두 조건자를 만족하지 않으면 true를 반환한다.
all()은 컬렉션의 모든 요소가 조건자를 만족해야 true를 반환한다.
val numbers = listOf("one", "two", "three", "four")
// 리스트의 문자열 중에 e로 끝나는 것이 하나라도 있으면 true를 반환한다
println(numbers.any { it.endsWith("e") }) // true
// 리스트의 문자열 중에 a로 끝나는 것이 하나도 없으면 true를 반환한다
println(numbers.none { it.endsWith("a") }) // true
// 리스트의 문자열 모두 e로 끝나면 true를 반환한다
println(numbers.all { it.endsWith("e") }) // false
any()와 none()은 조건자 없이도 사용이 가능하다. 이런 경우 컬렉션이 비어있는지 요소가 있는지 여부를 확인하는데 사용될 수 있다.
컬렉션이 비어있으면 any()는 false를 출력하고 요소가 있으면 true를 출력한다.
반대로 none()은 컬렉션이 비어있으면 true를 요소가 있으면 false를 출력한다.
all()의 경우는 조건자 없이 사용이 불가능하지만 컬렉션이 비어있을 경우 무조건 true를 출력한다.
val numbers = listOf("one", "two", "three", "four")
val empty = emptyList<String>()
println(numbers.any()) // true
println(empty.any()) // false
println(numbers.none()) // false
println(empty.none()) // true
println(empty.all { it == "메롱" }) // true
2. 오늘 알게 된 것
- Kotlin에서 filter()와 같은 함수를 활용해 특정 조건으로 컬렉션을 필터링해 새로운 리스트를 만드는 법을 알게 되었다.
- 미리 위와 같은 함수를 알았더라면 이미 풀어봤던 문제들을 조금 더 쉽게 풀 수 있었을 것 같다. 문제를 다시 풀어보아야 겠다.
- CPU의 성능을 향상시키기 위한 기법들에 대한 공부를 해보았다.