【Kotlin練習問題】コレクション flatMapでネスト構造をフラットにする

flatMapの使い方

コレクションの要素を操作したいとき、あるコレクションの要素がさらにリストになっているなど、ネスト構造を持つコレクションも少なくありません。コレクションの中のコレクションの要素にアクセスしたいときなどでも簡単にアクセスできるようにするための関数がKotlinには用意されています。

今回紹介するのはflatMap関数です。

flatMapは引数で指定した式や関数で要素を別のコレクションにしてくれます。引数に与えられた式や関数の戻り値である要素が全て格納されたリストが返されます。さっそく使用例を見てみましょう。

val containers = listOf(
    StringContainer(listOf("one", "two", "three")),
    StringContainer(listOf("four", "five", "six")),
    StringContainer(listOf("seven", "eight"))
)
println(containers.flatMap { it.values })
//[one, two, three, four, five, six, seven, eight]

上のコード中のcontainersというリストは、3つのStringContainerというリストを要素として持っています。つまりリストの中にリストがあるネスト構造になっていますが、flatMapをcontainersに使ってあげることによって、3つのStringContainerの要素全てを要素にもつ新しいリストを生成しています。

新しくできた要素には元のリストの要素がネストされていない状態で入っているため、普通のリストのように処理ができるようになっています。

問題

flatMapを用いて、以下の二つの関数を問題コードに実装してください。

  1. 与えられた顧客が注文した商品を全て返すgetOrderedProducts()
  2. 一人からでも注文された商品を全て返すgetOrderedProducts()

なお、全ての関連するクラスはShop.ktに含まれています。

問題コード:

//与えられた顧客が注文した商品を全て返す
fun Customer.getOrderedProducts(): List<Product> =
        TODO()

//一人からでも注文された商品を全て返す
fun Shop.getOrderedProducts(): Set<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
}
[expander_maker id=”1″ more=”ヒント” less=”非表示”]

ヒント

val result = listOf("abc", "12")
    .flatMap { it.toList() }

result == listOf('a', 'b', 'c', '1', '2')

flatMapは上記のようにして引数でフラット化したい対象を指定します。(上記例はネストではありませんが、文字列中の文字を一文字ずつフラット化しています)

また二つ目の関数に関しては、一つ目に実装したgetOrderedProducts()をflatMapの中で使うことによって簡単に実装することができます。

[/expander_maker] [expander_maker id=”1″ more=”答え” less=”非表示”]

答え

//与えられた顧客が注文した商品を全て返す
fun Customer.getOrderedProducts(): List<Product> =
    orders.flatMap { it.products }

//一人からでも注文された商品を全て返す
fun Shop.getOrderedProducts(): Set<Product> =
        customers.flatMap { it.getOrderedProducts() }.toSet()

別解

//与えられた顧客が注文した商品を全て返す
fun Customer.getOrderedProducts(): List<Product> =
    orders.flatMap(Order::products)

//一人からでも注文された商品を全て返す
fun Shop.getOrderedProducts(): Set<Product> =
    customers.flatMap(Customer::getOrderedProducts).toSet()

[解説]

①最終的にフラットにして利用したいのは顧客のもつ商品情報(Products)ですので、flatMapの引数にはproductsを指定します。

②一問目で実装したgetOrderedProducts()は顧客が注文した商品を全て返しているので、これを引数で全ての顧客上で呼び出すことによって、全ての顧客が注文した商品を全て取り出すことができます。つまり誰かが一度でも注文した商品は全てここに含まれていることになります。

戻り値の型はSetですので、忘れずにtoSet()で変換しましょう。

[/expander_maker]

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

完全無料で通えるプログラミングスクール

プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。

効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。

中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!

https://codelabsjp.net/best-programming-school/

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]