Develop!/Kotlin

컬렉션에 대한 연산 in Kotlin

체리필터 2023. 9. 25. 16:23
728x90
반응형

람다가 가독성을 올려주는 이유 중의 하나는 컬렉션에 대한 연산을 한 번에 수행할 수 있어서이다. 이미 살펴본 map, filter, any, foreach 와 같은 것들도 이러한 연산을 해 주는 것들 중 하나이다.

우선 list 를 만드는 방법 부터 살펴보자

fun createList() {
    val list1 = List(10) { it }
    println(list1)

    val list2 = List(10) { 0 }
    println(list2)

    val list3 = List(10) { 'a' + it }
    println(list3)

    val list4 = List(10) { list3[it%3] }
    println(list4)

    val mutableList1 = MutableList(5, { 10 * (it + 1) })
    println(mutableList1)

    val mutableList2 = MutableList(5) { 10 * (it + 1) }
    println(mutableList2)
}

list를 몇 개 만들지에 대한 파라미터를 넘긴 후 술부(predicate)에는 어떤 방식으로 초기화 할지를 전달하게 된다.

mutable list도 마찬가지이다. 두 번째 파라미터인 init은 별도로 빼서 람다 형식으로 적어도 된다.

이렇게 만들어진 list들에 적용할 수 있는 람다 함수를 하나씩 테스트 해 볼 수 있다.

fun predicate() {
    val list = listOf(-3, -1, 5, 7, 10)

    println(list.filter { it > 0 })
    println(list.count { it > 0 })
    println(list.find { it > 0 })
    println(list.firstOrNull { it > 0})
    println(list.lastOrNull { it < 0})

    println(list.any { it > 0 })
    println(list.any { it != 0})

    println(list.all { it > 0 })
    println(list.all { it != 0 })

    println(list.none { it > 0 })
    println(list.none { it == 0 })
}

filter, count는 조건에 맞는 것만 추출하고, find, firstOrNull은 조건에 맞는 첫 번째 값을 리턴한다. find는 찾는 값이 없을 경우 예외를 발생시키지만 firstOrNull은 값이 없을 경우 null을 반환한다.

firstOrNull과 반대되는 것은 lstOrNull 이다. 마지막 값을 찾아낸다.

any는 주어진 컬렉션에서 해당 되는 값이 한 개라도 있으면 true를, all은 모든 원소가 해당 되면 true를, none은 한 개의 원소도 해당되지 않으면 true를 리턴한다.

filter의 반대 개념으로 filterNot을 쓸 수 있다.

fun partition() {
    val list = listOf(-3, -1, 5, 7, 10)
    val isPositive = { i:Int -> i > 0 }

    val positiveResult = list.filter(isPositive)
    val negativeResult = list.filterNot(isPositive)
    println(positiveResult)
    println(negativeResult)

    val (pos, neg) = list.partition(isPositive)
    println(pos)
    println(neg)
}

isPositive라는 predicate를 만들고 filter, filterNot으로 두 가지 값을 받아 출력해 보면 양수, 음수가 잘 출력 되는 것을 볼 수 있다.

이러한 경우 partition 이라는 함수로 Pair 쌍을 값으로 받을 수 있다. 위의 코드에서 해당 내용을 확인해 볼 수 있다. 결과는 아래와 같다.

null인 것을 제외하고 리턴해 주는 이미 잘 만들어진 filter도 있다. 다음과 같이 사용하면 된다.

fun filterNotNull() {
    val list = listOf(1, 2, null)
    println(list.filterNotNull())
}

리스트의 원소가 숫자일 경우 사용할 수 있는 sum, sorted와 같음 함수가 있지만 숫자가 아닌 경우에는 sumBy, sortedBy와 같은 함수를 사용할 수 있다.

fun byOperation() {
    val products = listOf(
        Product("bread", 2.0),
        Product("wine", 5.0)
    )
    val sumByResult = products.sumByDouble { it.price }
    val sumOfresult = products.sumOf { it.price }
    println(sumByResult)
    println(sumOfresult)

    val sortedByDescendingResult = products.sortedByDescending { it.price }
    println(sortedByDescendingResult)

    val minByOrNullResult = products.minByOrNull { it.price }
    println(minByOrNullResult)
}

결과는 아래와 같다.

sum 또는 sort를 Product의 price 필드를 보고 결정하도록 만들었다.

다만 sumBy 함수는 deprecated 되어 있다. overflow가 발생해서 문제가 발생하기 때문이다. 따라서 이런 걱정을 안하고 사용하려면 sumOf를 사용할 수 있다.

list 안에 있는 값을 취하거나 버릴 경우에는 take, drop을 쓸 수 있다. take는 조건에 맞는 것을 취하게 되고 drop은 조건에 맞는 것을 버리게 된다.

fun takeOrDrop() {
    val list = listOf('a', 'b', 'c', 'X', 'Z')

    val takeLastResult = list.takeLast(3)
    println(takeLastResult)

    val takeWhileResult = list.takeLastWhile { it.isUpperCase() }
    println(takeWhileResult)

    val dropResult = list.drop(1)
    println(dropResult)

    val dropWhileResult = list.dropWhile { it.isLowerCase() }
}

결과는 아래와 같다.

다음의 경우에는 Set에도 적용할 수 있다.

fun setOperation() {
    val set = setOf("a", "ab", "ac")
    val length = set.maxByOrNull { it.length }?.length
    println(length)

    val filterResult = set.filter { it.contains('b') }
    println(filterResult)
}

maxOrNull은 null을 반환할 수 있으므로 lenght를 구하기 전에 ?를 붙여 준다.

filter, map을 set에 적용할 경우 결과값은 List가 되게 된다.

728x90
반응형

'Develop! > Kotlin' 카테고리의 다른 글

고차함수, 리스트 조작 in Kotlin  (0) 2023.10.04
멤버참조 in Kotlin  (0) 2023.09.27
람다 in Kotlin  (0) 2023.09.25
제네릭스 in Kotlin  (0) 2023.09.22
안전한 호출과 엘비스 연산자 in kotlin  (0) 2023.09.14