Kotlin KoansでKotlin入門　第２７回：Max min

# はじめに
公式の問題集[「Kotlin Koans」](https://play.kotlinlang.org/koans/overview)を解きながらKotlinを学習します。

過去記事はこちら
- Introduction
  - [第０回：Kotlin Koansについて](https://qiita.com/kosuke1/items/41d192af63485bfc8cb8)
  - [第１回：Simple Functions](https://qiita.com/kosuke1/items/5862a3ece96082a62e67)
  - [第２回：Named arguments](https://qiita.com/kosuke1/items/2af11262852ee41b5da7)
  - [第３回：Default arguments](https://qiita.com/kosuke1/items/16cb8ac780df58dc8054)
  - [第４回：Triple-quoted strings](https://qiita.com/kosuke1/items/52cfeed2c49b0252d40e)
  - [第５回：String templates](https://qiita.com/kosuke1/items/0221bbbc5bc70847be6f)
  - [第６回：Nullable types](https://qiita.com/kosuke1/items/aa96e422c5313b6961bc)
  - [第７回：Nothing type](https://qiita.com/kosuke1/items/1ca0e48820d58c9dfe16)
  - [第８回：Lambdas](https://qiita.com/kosuke1/items/8de74f9491e003127f53)
- Classes
  - [第９回：Data classes](https://qiita.com/kosuke1/items/1be2de1b4406b8d914bf) 
  - [第１０回：Smart casts](https://qiita.com/kosuke1/items/50bdecaa0740eb0e1a17)
  - [第１１回：Sealed classes](https://qiita.com/kosuke1/items/71a4da02fe1e99da5d63)
  - [第１２回：Rename on import](https://qiita.com/kosuke1/items/f11465cc15a94e7589a3)
  - [第１３回：Extension functions](https://qiita.com/kosuke1/items/d4a1da00bc142c297221)
- Conventions
  - [第１４回：Comparison](https://qiita.com/kosuke1/items/e9486d044cae32167f36) 
  - [第１５回：Ranges](https://qiita.com/kosuke1/items/797f049ce5f33b1810c9)
  - [第１６回：For loop](https://qiita.com/kosuke1/items/f5f6eeea41fb89b2a20c)
  - [第１７回：Operators overloading](https://qiita.com/kosuke1/items/9845f98a632121f1493b)
  - [第１８回：Invoke](https://qiita.com/kosuke1/items/7879f83341d169374eb3)
- Collections
  - [第１９回：Collections Introduction](https://qiita.com/kosuke1/items/af91a0bd505cd93a3900) 
  - [第２０回：Sort](https://qiita.com/kosuke1/items/ed9a4b36f8573fc65a8f)
  - [第２１回：Filter map](https://qiita.com/kosuke1/items/45e70bac73706643e40c)
  - [第２２回：All, Any, and other predicates](https://qiita.com/kosuke1/items/daa940bf57e93d198ce4)
  - [第２３回：Associate](https://qiita.com/kosuke1/items/a4d8b0701b6848d43473)
  - [第２４回：Group By](https://qiita.com/drafts/d2279add0e79cbe19f01/edit)
  - [第２５回：Partition](https://qiita.com/kosuke1/items/d2279add0e79cbe19f01)
  - [第２６回：FlatMap](https://qiita.com/kosuke1/items/f08f22de98d736df6e12)


# 問題
[Max min](https://play.kotlinlang.org/koans/Collections/Max%20min/Task.kt)

[Aggregate operations](https://kotlinlang.org/docs/collection-aggregate.html)について学びます。
2つの関数を実装します。
- 最初の関数は、ショップで最も多くの注文を出した顧客を返すこと。
- 2つ目は、指定された顧客が注文した中で最も高価な商品を返すことです。

関数maxOrNull、minOrNull、maxByOrNull、minByOrNullが役に立つかもしれません。

```Kotlin
listOf(1, 42, 4).maxOrNull() == 42
listOf("a", "ab").minByOrNull(String::length) == "a"
```
ラムダ式の代わりに[callable references(呼び出し可能な参照)](https://kotlinlang.org/docs/lambdas.html#instantiating-a-function-type)を使用することができます。特に、異なるラムダで発生し、異なる型を持つコールチェーンで役に立ちます。getMostExpensiveProductBy関数をcallable referencesを使用して実装します。


```修正前コード.kt
// ショップで最も多くの注文を出した顧客を返す
fun Shop.getCustomerWithMaxOrders(): Customer? =
        TODO()

// 指定された顧客が注文した中で最も高価な商品を返す
fun getMostExpensiveProductBy(customer: Customer): Product? =
        TODO()
```


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

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

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

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

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


# 問題のポイント
minOrNull() と maxOrNull() はそれぞれ最小の要素と最大の要素を返します。
空のコレクションでは、これらはnullを返します。


```Kotlin
fun main() {
    val numbers = listOf(6, 42, 10, 4)

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

また、特定のセレクタ関数やカスタムComparatorによって、最小・最大の要素を取得する機能もあります。
- maxByOrNull()とminByOrNull()はセレクタ関数を受け取り、その関数が最大または最小の値を返す要素を返します。
- maxWithOrNull()およびminWithOrNull()はComparatorオブジェクトを取り、そのComparatorに従って最大または最小の要素を返します。
- maxOfOrNull()、minOfOrNull()はセレクタ関数を受け取り、そのセレクタ自体の最大または最小の戻り値を返します。
- maxOfWithOrNull() および minOfWithOrNull() は Comparator オブジェクトを取り、その Comparator に従って最大または最小のセレクタの戻り値を返します。


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

# 解答例
```Kotlin
// ショップで最も多くの注文を出した顧客を返す
fun Shop.getCustomerWithMaxOrders(): Customer? = 
    customers.maxByOrNull { it.orders.size }

// 指定された顧客が注文した中で最も高価な商品を返す
fun getMostExpensiveProductBy(customer: Customer): Product? = 
    customer.orders.flatMap(Order::products).maxByOrNull(Product::price)

```