18강. 코틀린에서 컬렉션을 함수형으로 다루는 방법
- filter와 map
- 다양한 컬렉션 처리 기능
- groupBy / associateBy
- flatten / flatMap
filter와 map
fun main() {
val fruits = listOf(
Fruit("사과", 1_000),
Fruit("사과", 1_200),
Fruit("사과", 1_200),
Fruit("사과", 1_500),
Fruit("바나나", 3_000),
Fruit("바나나", 3_200),
Fruit("바나나", 2_500),
Fruit("수박", 10_000),
)
val apples = fruits.filter { fruit -> fruit.name == "사과" }
// filter 에서 인덱스가 필요하다면 filterIndexed 사용합니다.
val apples2 = fruits.filterIndexed { idx, fruit ->
println(idx)
fruit.name == "사과"
}
// 사과의 가격들을 알기위해 filter 이후 map을 수행합니다.
val applePrices = fruits.filter { fruit -> fruit.name == "사과" }
.map { fruit -> fruit.price }
// map에서 인덱스가 필요하다면 mapIndexed 를 사용합니다.
val applePrices2 = fruits.filter { fruit -> fruit.name == "사과" }
.mapIndexed { idx, fruit ->
println(idx)
fruit.price
}
}
Mapping의 결과가 null이 아닌 것만 가져오고 싶은 경우, mapNotNull을 사용하면 됩니다.
val values = fruits.filter { fruit -> fruit.name == "사과" }
.mapNotNull { fruit -> fruit.nullOrVale() }
다양한 컬렉션 처리 기능
all: 조건을 모두 만족하면 true, 그렇지 않으면 false 입니다.
val isAllApple = fruits.all { fruit -> fruit.name == "사과" }
none: 조건을 모두 불만족하면 true, 그렇지 않으면 false 입니다.
val isNoApple = fruits.none { fruit -> fruit.name == "사과" }
any: 조건을 하나라도 만족하면 true, 그렇지 않으면 false 입니다.
val isBigPrice = fruits.any { fruit -> fruit.price >= 10_000 }
count: 개수를 셉니다.
val fruitCount = fruits.count()
sortedBy: (오름차순) 정렬을 합니다.
val sortedByFruit = fruits.sortedBy { fruit -> fruit.price }
sortedByDescending: (내림차순) 정렬을 합니다.
val sortedByDescFruit = fruits.sortedByDescending { fruit -> fruit.price }
distinctby: 변형된 값을 기준으로 중복을 제거합니다.
val distinctFruitNames = fruits.distinctBy { fruit -> fruit.name }
.map { fruit -> fruit.name }
first: 첫번째 값을 가지고 옵니다. 값이 없을 경우 예외가 발생합니다.
firstOrNull: 첫번째 값 또는 null을 가지고 옵니다.
last: 마지막 값을 가지고 옵니다. 값이 없을 경우 예외가 발생합니다.
lastOrNull: 마지막 값 또는 null을 가지고 옵니다.
groupBy / associateBy
groupBy는 특정 key로 key에 해당하는 객체들을 매핑할 때 사용합니다. 다음은 과일이름을 key로, key에 해당하는 과일들을 value로 하는 Map을 생성하는 코드입니다.
val map: Map<String, List<Fruit>> = fruits.groupBy { fruit -> fruit.name }
key와 value를 동시에 처리할 수도 있습니다. 다음은 과일이름을 key로, key에 해당하는 가격들을 value로 하는 Map을 생성하는 코드입니다.
val map2: Map<String, List<Int>> =
fruits.groupBy({ fruit -> fruit.name }, { fruit -> fruit.price })
중복되지 않은 key를 가지고 Map을 생성할 때는 associateBy를 사용할 수 있습니다.
// Fruit 객체 내 중복되지 않는 id 필드가 필요합니다.
val map: Map<Long, Fruit> = fruits.associateBy { fruit -> fruit.id }
val map2: Map<Long, Long> = fruits
.associateBy({ fruit -> fruit.id }, { fruit -> fruit.price })
Map에 대해서도 앞선 기능들을 대부분 사용할 수 있습니다. 다음은 groupBy로 생성한 map에 filter 를 사용한 코드입니다.
val map: Map<String, List<Fruit>> = fruits.groupBy { fruit -> fruit.name }
.filter { (key, value) -> key == "사과" }
flatten
중첩되어 있는 컬렉션을 중첩 해제하기 위해 사용합니다. 즉, 평탄화 합니다. 다음은 List<List<>>를 List<> 형태로 평탄화 합니다.
// flatten
val numbers = listOf(listOf(1,2,3), listOf(4, 5), listOf(6))
val result = numbers.flatten()
println(result) // [1, 2, 3, 4, 5, 6]
flatMap
인자로 주어진 람다를 컬렉션에 적용하고 flatten 합니다.
val numbers = listOf(listOf(1, 2, 3), listOf(4, 5), listOf(6))
val result2 = numbers.flatMap { it.map { "number $it" } }
println(result2) // [number 1, number 2, number 3, number 4, number 5, number 6]
평탄화 작업만 필요한 경우 flatten을 사용하고, 별도 처리가 필요한 경우 flatMap을 사용하면 됩니다.
감사합니다.