はじめに
公式の問題集「Kotlin Koans」を解きながらKotlinを学習します。
過去記事はこちら
- Introduction
- Classes
- Conventions
- Collections
問題
アソシエーションについて学びます。
associateBy、associateWith、associate を使って、以下の関数を実装します。
- 顧客名をキーとする顧客のmapを作成します。
- 顧客をキーとする顧客の都市のmapを作成します。
- 顧客名をキーとする顧客の都市のmapを作成します。
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)
// 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()
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に残ります。
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateWith { it.length }) // {one=3, two=3, three=5, four=4}
コレクション要素を値とするマップを構築するために、associateBy()という関数があります。
これは、要素の値に基づいてキーを返す関数を受け取ります。
2つの要素のキーが等しい場合、最後の1つだけがマップに残ります。
associateBy()は、値変換関数と一緒に呼び出すこともできます。
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() です。
これはラムダ関数を受け取り、対応するマップエントリのキーと値をペアで返します。
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}
解答例
// 顧客名をキーとする顧客の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 }