Kotlin KoansでKotlin入門　第２３回：Associate

# はじめに
公式の問題集[「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://play.kotlinlang.org/koans/Collections/Associate/Task.kt)

[アソシエーション](https://kotlinlang.org/docs/collection-transformations.html?&_ga=2.40100263.440373815.1650897634-1976109019.1644486873#associate)について学びます。
associateBy、associateWith、associate を使って、以下の関数を実装します。
- 顧客名をキーとする顧客のmapを作成します。
- 顧客をキーとする顧客の都市のmapを作成します。
- 顧客名をキーとする顧客の都市のmapを作成します。

```Kotlin
val list = listOf("abc", "cdef")
list.associateBy { it.first() } == mapOf('a' to "abc", 'c' to "cdef")
list.associateWith { it.length } == mapOf("abc" to 3, "cdef" to 4)
list.associate { it.first() to it.length } == mapOf('a' to 3, 'c' to 4)
```

```修正前コード.kt
// Build a map from the customer name to the customer
fun Shop.nameToCustomerMap(): Map<String, Customer> =
        TODO()

// Build a map from the customer to their city
fun Shop.customerToCityMap(): Map<Customer, City> =
        TODO()

// Build a map from the customer name to their city
fun Shop.customerNameToCityMap(): Map<String, City> =
        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
}
```

# 問題のポイント

アソシエーション変換は、コレクション要素とそれに関連する特定の値からマップを構築することを可能にします。
異なるアソシエーションタイプでは、要素はアソシエーションマップのキーまたは値のいずれかになることができます。

associateWith() は、元のコレクションの要素をキーとし、与えられた変換関数によってそれらから値を生成するマップを作成します。
2つの要素が等しい場合、最後の1つだけがMapに残ります。

```Kotlin
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateWith { it.length }) // {one=3, two=3, three=5, four=4}
```

コレクション要素を値とするマップを構築するために、associateBy()という関数があります。
これは、要素の値に基づいてキーを返す関数を受け取ります。
2つの要素のキーが等しい場合、最後の1つだけがマップに残ります。
associateBy()は、値変換関数と一緒に呼び出すこともできます。

```Kotlin
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateBy { it.first().uppercaseChar() }) // {O=one, T=three, F=four}
println(numbers.associateBy(keySelector = { it.first().uppercaseChar() }, valueTransform = { it.length })) // {O=3, T=5, F=4}

```
キーと値の両方が何らかの方法でコレクション要素から生成されるマップを構築するもうひとつの方法は、関数 associate() です。
これはラムダ関数を受け取り、対応するマップエントリのキーと値をペアで返します。

```Kotlin
val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell")
println(names.associate { name -> parseFullName(name).let { it.lastName to it.firstName } }) // {Adams=Alice, Brown=Brian, Campbell=Clara}

```



# 解答例
```Kotlin
// 顧客名をキーとする顧客のmapを作成
fun Shop.nameToCustomerMap(): Map<String, Customer> =
        customers.associateBy { it.name }

// 顧客をキーとする顧客の都市のmapを作成
fun Shop.customerToCityMap(): Map<Customer, City> =
        customers.associateWith { it.city }

// 顧客名をキーとする顧客の都市のmapを作成
fun Shop.customerNameToCityMap(): Map<String, City> =
       customers.associate { it.name to it.city }
```