BackEnd/Kotlin

18강. 코틀린에서 컬렉션을 함수형으로 다루는 방법

hanseom 2024. 4. 14. 08:00
반응형
  1. filter와 map
  2. 다양한 컬렉션 처리 기능
  3. groupBy / associateBy
  4. 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을 사용하면 됩니다.

 

 

 

감사합니다.

반응형