【Kotlin練習問題】コレクションの集計操作(最大値・最小値・合計等を求める方法)

Kotlin

コレクションの集計操作

Kotlinのコレクションには集計操作に使われる関数が用意されています。それらの関数はコレクションの中身に基づいて一つの値を返すようになっています。

関数のほとんどは有名なもので、他のプログラミング言語と同じような使われ方がなされています。

最大・最小・平均・合計・要素数

リストなどのコレクションの集計操作ではよく要素の数であったり、最大値などを求めることが多いかと思われます。Kotlinでも当然それ用の関数が用意されているのでそちらの使い方を紹介します。

  1. minOrNull()とmaxOrNull():それぞれ要素の最小値と最大値を返します。空のコレクションに対してはnullを返します。
  2. average():数値のコレクションの要素の平均値を返します。
  3. sum():数値のコレクションの要素の合計の値を返します。
  4. count():コレクションの要素の数を返します。

使い方は全く難しくありません。以下のコードにそれぞれの使用例を挙げておきます。

fun main() {
    val numbers = listOf(2, 81, 40, 9)

    println("要素数: ${numbers.count()}")
    println("最大値: ${numbers.maxOrNull()}")
    println("最小値: ${numbers.minOrNull()}")
    println("平均値: ${numbers.average()}")
    println("合計値: ${numbers.sum()}")
}

//要素数: 4
//最大値: 81
//最小値: 2
//平均値: 33.0
//合計値: 132

セレクター関数とカスタムComparatorを用いて最大・最小値を取得する

上記であげた関数の他にもセレクター関数(引数に式を取る)やカスタムComparator(指定した比較方法)を用いて、その結果の最大値や最小値を返すための関数もあります。

  1. maxByOrNull()とminByOrNull():それぞれセレクター関数を取り、その結果の最大値または最小値を返します。
  2. maxWithOrNull()とminWithOrNull():Comparatorオブジェクトを取り、そのComparatorに基づいて最大値・最小値を返します。

上記の関数も全て空のコレクションに対してはnullを返します。

またmaxOf()minOf()という関数もそれぞれmaxByOrNull()minByOrNull()の代わりに使うこともできますが、空のコレクションに対してはNoSuchElementExceptionという例外を投げる点で異なります。

val numbers = listOf(8, 6, 12, 19)
val min3Remainder = numbers.minByOrNull { it % 3 }
println(min3Remainder)    //6

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

上記コード中のmin3Remainderでは、それぞれの要素を3で割った結果、割り切れるもの中で一番小さい値を返すというセレクター関数です。

longestStringは名前の通り文字数が一番長い要素を返します。

問題

問題コードに一番注文をしている顧客を返す関数と、対象顧客が注文した中で一番高額な商品を返す関数を実装してください。

なお、Shopクラスと全ての関連するクラスはShop.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
}

ヒント

ラムダ式の代わりにcallable references(コーラブル参照)というものを使うこともできます。これは it が異なるラムダ式や違う型の中で現れたりする際にコールチェーンの中で使用すると特に便利です。今回はgetMostExpensiveProductBy関数の中で使うことをお勧めします。

callable referencesを使うと、関数やプロパティ、コンストラクタなどに対する参照を呼び出したり関数型のインスタンスとして使うことができます。

例えば、以下のようなisOddという関数があります。

fun isOdd(x: Int) = x % 2 != 0

直接isOdd(5)などのように呼び出すことも当然できますが、他の関数に渡して関数型の値として使うことができます。そのためには :: 演算子を使います。

val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))
//[1, 3]

これを今回のgetMostExpensiveProductBy関数に活用して

customer.orders
    .flatMap(Order::products)
    .maxByOrNull(Product::price)

のような形で目的の値までたどり着くことができます。

ヒント

答え

//一番注文をしている顧客を返す
fun Shop.getCustomerWithMaxOrders(): Customer? =
    customers.maxByOrNull {it.orders.size}

//注文した中で一番高額な商品を返す
fun getMostExpensiveProductBy(customer: Customer): Product? =
    customer.orders
                .flatMap(Order::products)
                .maxByOrNull(Product::price)
答え

その他の問題はこちらからどうぞ。

おすすめ書籍

Kotlinの文法をまず学びたい!という方には以下の書籍がおすすめです。Kotlinは日本語書籍がまだ豊富とは言えない状況ですが、細かく解説されており、Kotlin入門者のかたでもつまずくことなく学習できると思います。

実際にアプリを作りながら覚えていきたい!という方には以下もお勧めです。はじめに上の書籍で文法をさらっと学んでから取り組むのがお勧めです。

プロフィール

プロフィール
コードラボJP

大学卒業後SEに就職、現在は退職しフリーランスとして活動中。
『初心者でも挫折せずに一人でプログラミングを学べる』をモットーに、コードラボJPを開設
お問い合わせ等はcodelabsjp@gmail.comまで

コードラボJPをフォローする
タイトルとURLをコピーしました