반응형

https://play.kotlinlang.org/koans/Collections/Introduction/Task.kt

 

Kotlin Playground: Edit, Run, Share Kotlin Code Online

 

play.kotlinlang.org

코틀린 공식문서에 있는 Colletions 파트를 읽으며 정리해보았습니다.


Max Min

Aggregate operations

코틀린 컬렉션은 흔히 쓰이는 aggregate operations를 포함한다. 코틀린에 있는 대부분의 aggregate operations는 잘 알려져있고 타 언어와 같은 방식으로 동작한다.

  • minOrNull()과 maxOrNull()는 각각 가장 작은 값과 큰 값을 반환한다.
  • average()는 컬렉션에 있는 숫자 원소들의 평균을 반환한다.
  • sum()은 컬렉션에 있는 숫자 원소들의 합을 반환한다.
  • count()는 컬렉션에 있는 원소들의 수를 반환한다.
fun main() {
    val numbers = listOf(6, 42, 10, 4)

    println("Count: ${numbers.count()}") // print => Count: 4
    println("Max: ${numbers.maxOrNull()}") // print => Max: 42
    println("Min: ${numbers.minOrNull()}") // print => Min: 4
    println("Average: ${numbers.average()}") // print => Average: 15.5
    println("Sum: ${numbers.sum()}") // print => Sum: 62
}

특정한 selector 함수나 custom Comparator을 사용하여 최솟값과 최댓값을 구할 수 있는 함수도 있다.

  • maxByOrNull()과 minByOrNull()은 selector 함수를 받고 가장 크거나 가장 작은 값을 반환한다.
  • maxWithOrNull()과 minWithOrNull()은 Comparator 객체를 받고 가장 크거나 가장 작은 원소를 Comparator에 따라 반환한다.
  • maxOfOrNull()과 minOrNull()은 selector함수를 받고 selector 자체의 최댓값이나 최솟값을 반환한다.
  • maxOfWithOrNull()과 minOrWithOrNull()은 Comparator 객체를 받고 가장 크거나 작은 selector를 Comparator에 따라 반환한다.

이 함수들은 null이나 빈 컬렉션을 반환할 수있다. 대안으로 maxOf(), minOf(), maxOfWith()와 minOfWith가 있다. 위의 함수들과 동일한 작업을 하지만 빈 컬렉션일 때 NoSuchElementException을 발생시킨다.

val numbers = listOf(5, 42, 10, 4)
val min3Remainder = numbers.minByOrNull { it % 3 }
println(min3Remainder) // print => 42

val strings = listOf("one", "two", "three", "four")
val longestString = strings.maxWithOrNull(compareBy { it.length })
println(longestString) // print => three

Basic Code

Shop.kt 의 코드 구성

data class City(val name: String) {
    override fun toString() = name
}

data class Product(val name: String, val price: Double) {
    override fun toString() = "'$name' for $price"
}

data class Order(val products: List<Product>, val isDelivered: Boolean)

data class Customer(val name: String, val city: City, val orders: List<Order>) {
    override fun toString() = "$name from ${city.name}"
}

data class Shop(val name: String, val customers: List<Customer>)

문제

// Return a customer who has placed the maximum amount of orders
fun Shop.getCustomerWithMaxOrders(): Customer? =
        TODO()	

// Return the most expensive product that has been ordered by the given customer
fun getMostExpensiveProductBy(customer: Customer): Product? =
        TODO()

정답

// Return a customer who has placed the maximum amount of orders
fun Shop.getCustomerWithMaxOrders(): Customer? =
        customers.maxByOrNull { it.orders.size }

// Return the most expensive product that has been ordered by the given customer
fun getMostExpensiveProductBy(customer: Customer): Product? =
        customer.orders
                .flatMap(Order::products)
                .maxByOrNull(Product::price)

문제

// Return the sum of prices for all the products ordered by a given customer
fun moneySpentBy(customer: Customer): Double =
        TODO()

정답

// Return the sum of prices for all the products ordered by a given customer
fun moneySpentBy(customer: Customer): Double =
        customer.orders.flatMap { it.products }.sumOf { it.price }

Fold and reduce

reduce()함수와 fold()함수는 주어진 작업을  컬렉션 원소들에 순차적으로 적용하고 누적된 값을 반환하는 함수입니다.

이 작업은  이전의 축전된 값과 컬렉션의 원소 이렇게 2개의 인자를 가집니다.

fold()함수는 초기값을 받고 그 값을 첫 단계에서 값에 축적합니다. 반면 reduce()함수는 첫 단계에서 첫 번째와 두 번째 원소를 작업 인자로 사용합니다.

val numbers = listOf(5, 2, 10, 4)

val simpleSum = numbers.reduce { sum, element -> sum + element }
println(simpleSum) // print => 21
val sumDoubled = numbers.fold(0) { sum, element -> sum + element * 2 }
println(sumDoubled) // print => 42

위의 예시 코드에서 fold()는 원소들 * 2를 계산하였고 동일함 함수(람다식)가 reduce()에 들어간다면 다른 결과가 도출된다(37이 출력됨). 왜냐하면 reduce()는 리스트의 첫번째와 두번째 인자를 첫 단계의 인자로 사용하기 때문이다. 따라서 첫 번째 원소는 * 2가 적용되지 않는다.

 

원소를 역순으로 정리해서 작업하고 싶으면 reduceRight()과 foldRight()를 사용하면 된다. fold()와 reduce()와 비슷하게 작업하는 것 처럼 보이지만 ~~Right()수는 원소의 마지막부터 계산한다. 또한 reduce()와 fold()의 람다식 인자는 sum, element 순 이었지만 reduceRight()과 foldRight()의 람다식 인자는 element, sum 순이다.

val numbers = listOf(5, 2, 10, 4)
val sumDoubledRight = numbers.foldRight(0) { element, sum -> sum + element * 2 }
println(sumDoubledRight) // print => 42

 

reduceIndexed()와 foldIndexed()를 이용하면 원소의 인덱스도 람다식 파라미터로 이용할 수 있다.

역순으로 이용하고 싶다면 마찬가지로 reduceRightIndexed()와 foldRightIndexed()를 이용할 수 있다.

val numbers = listOf(5, 2, 10, 4)
val sumEven = numbers.foldIndexed(0) { idx, sum, element -> if (idx % 2 == 0) sum + element else sum }
println(sumEven) // print => 15

val sumEvenRight = numbers.foldRightIndexed(0) { idx, element, sum -> if (idx % 2 == 0) sum + element else sum }
println(sumEvenRight) // print => 15

위의 함수들은 빈 컬렉션일 경우 예외를 발생시킨다. 예외 대신 null을 반환받고 싶다면 아래의 함수들을 이용하면 된다.

  • reduceOrNull()
  • reduceRightOrNull()
  • reduceIndexedOrNull()
  • reduceRightIndexedOrNull()

중간까지 계산된 값을 저장하고 싶다면 runningFold() 혹은 runningReduce()를 사용하면 된다.

val numbers = listOf(0, 1, 2, 3, 4, 5)
val runningReduceSum = numbers.runningReduce { sum, item -> sum + item }
val runningFoldSum = numbers.runningFold(10) { sum, item -> sum + item }
// print like below
// Sum of first N elements with runningReduce:
// N = 1: 0
// N = 2: 1
// N = 3: 3
// N = 4: 6
// N = 5: 10
// N = 6: 15
// Sum of first N elements with runningFold:
// N = 1: 10
// N = 2: 10
// N = 3: 11
// N = 4: 13
// N = 5: 16
// N = 6: 20
// N = 7: 25

문제

// Return the set of products that were ordered by all customers
fun Shop.getProductsOrderedByAll(): Set<Product> {
    TODO()
}

fun Customer.getOrderedProducts(): List<Product> =
        TODO()

정답

// Return the set of products that were ordered by all customers
fun Shop.getProductsOrderedByAll(): Set<Product> {
    val allProducts = customers.flatMap { it.getOrderedProducts() }.toSet()
    return customers.fold(allProducts) { orderedByAll, customer ->
        orderedByAll.intersect(customer.getOrderedProducts())
    }
}

fun Customer.getOrderedProducts(): List<Product> =
        orders.flatMap(Order::products)
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기