reduce()とfold()による集計操作

コレクションの集計操作(最大値や最小値などを求める操作)に関しては以下の記事でも解説しました。

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

しかし、上記事で紹介した関数以外にも、reduce()fold()という関数があります。

これらは与えられた処理をコレクションの要素に順次行っていき、蓄積された結果を返り値として返します。処理では二つの引数を取り、一つ目が蓄積された値で二つ目がコレクションの要素です。

reduce()とfold()の違いは、fold()は初期値を設定することができ、それを一番最初に蓄積された値として使いますが、reduce()は一つ目の要素と二つ目の要素を最初の処理で引数として使います。

何はともあれ、まずは使い方を実際のコードで見てみるのが一番わかりやすいかと思います。

val numbers = listOf(5, 2, 10, 4)

val sum = numbers.reduce { sum, element -> sum + element }
println(sum) //21
val sumDoubled = numbers.fold(0) { sum, element -> sum + element * 2 }
println(sumDoubled) //42

reduceの一回目の処理ではsumとelementという引数にnumbersというリストの最初の要素(5)と二番目の要素(2)が入っています。二回目の処理ではsumに7、elementに10というように順に処理を行っていくことで要素全体の合計を求めることができます。

一方でfoldのほうでは引数に0を与えているので、sumに0が代入されます。elementには5が入っています。処理の中身ではelementに代入された値を二倍してsumに足しているので、最終的には要素全体を2倍した数の合計が求められます。

上記のfoldで行っている処理をreduceで書こうとして失敗した例が以下になります。

val numbers = listOf(5, 2, 10, 4)
val sumDoubledReduce = numbers.reduce { sum, element -> sum + element * 2 }
println(sumDoubledReduce) //37

何が間違っているかわかりますか?

これも全て二倍にして足しているように見えますが、実は一回目の処理でsumには5が代入されますが、5は二倍になっていないのです。だからといって処理の中身で sum * 2 + element * 2のようにしてしまうと、処理ごとに合計が2倍されてしまうため、求めていた結果は得られません。

このようにして行わせたい処理に応じてfoldとreduceを使い分けることが大切です。

問題

foldを使って全ての顧客によって注文された商品のセットを返す関数を実装してください。

また前回の問題(コレクション flatMapでネスト構造をフラットにする)で実装したCustomer.getOrderedProducts()を使うこともできます。

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

問題コード:

//全ての顧客によって注文された商品のセットを返す
fun Shop.getProductsOrderedByAll(): Set<Product> {
    TODO()
}

fun Customer.getOrderedProducts(): List<Product> =
    orders.flatMap(Order::products)

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=”非表示”]

ヒント

2つのリストの積集合(リストAにもリストBにも含まれる要素)を求めるにはintersect関数を用います。

使用例)

val listA = listOf(1, 2, 3)
val listB = listOf(3, 4, 5)

val result = listA.intersect(listB)
println(result) //[3]
[/expander_maker] [expander_maker id=”1″ more=”答え” less=”非表示”]

答え

//全ての顧客によって注文された商品のセットを返す
fun Shop.getProductsOrderedByAll(): Set<Product> {
    val allProducts = customers.flatMap { it.getOrderedProducts() }.toSet()
    return customers.fold(allProducts, { orderedByAll, customer ->
        orderedByAll.intersect(customer.getOrderedProducts())
    })
}

fun Customer.getOrderedProducts(): List<Product> =
    orders.flatMap(Order::products)

[解説]

初めにallProductsという定数に一人でも顧客が注文した商品全てのセットを保存します。

それをfoldの初期値に設定し、その要素とそれぞれの顧客が注文した商品で一致するものだけをorderedByAllという変数に格納していくことで、全ての顧客によって注文された商品のセットを作成することができます。

[/expander_maker]

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

この問題が解けた方は既に入門レベルを超えています。

次のステップとして、より実践的なKotlinスキルを身に着けたい方は以下の書籍がおすすめです。

[itemlink post_id=”2225″]

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

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

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

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

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

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″]

Partitionとは

Partition(パーティション)とは日本語で「分割」や「仕切り」を意味する英語です。

リストなどのコレクションを特定の条件下でフィルタリングすることはよくありますが、フィルタリングのための関数がKotlinには事前に用意されちえます。その一つが今回紹介するpartition()です。

partition()は引数に渡された条件式やラムダ関数に合致しない要素を別のリストとしてキープしてくれます。したがって、返り値はListのPairになります。一つ目のリストには条件に合致した要素が入っており、二つ目にはそれ以外の要素が入っています。

val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }

println(match)  //[three, four]
println(rest)   //[one, two]

Destructuring declaration(分解宣言)

Partitionの説明で挙げた上のコードの中には

val (match, rest) = numbers.partition { it.length > 3 }

という部分があります。これはDestructuring declaration(分解宣言)と呼ばれるもので、一つのオブジェクトの複数の変数に分解するために行っているものです。

例えば”名前”と”年齢”という属性を含んでいる”人間”というものを定義したいときに、いちいち一つずつ定義するのではなく、分解宣言を用いて以下のように表すことができます。

val (name, age) = person

ここでは一度に二つの変数(name, age)を宣言しており、それぞれ独立して使用することもできます。

上のコードはコンパイル時に以下のように解釈されます。

val name = person.component1()
val age = person.component2()

分解宣言の右辺にはcomponent関数を呼び出せる範囲内であればなんでも置くことができます。ですので、component3()やcomponent4()なども場合によってはあり得ます。

Point:componentN()関数を分解宣言で使用できるようにするためにはoperatorキーワードでマーキングされている必要があります。

問題

patitionを使って注文した商品の中でまだ配送されていない(undelivered)商品の数のほうが多い顧客を返す関数を実装してください。

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

問題コード:

//未配送の商品の方が多い顧客を返す
fun Shop.getCustomersWithMoreUndeliveredOrders(): Set<Customer> = 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=”非表示”]

ヒント

deliveredとundeliveredごとにリストを分ける必要があります。その際に分解宣言を使って代入します。分解宣言は以下のように使います。

val numbers = listOf(1, 3, -4, 2, -11)
val (positive, negative) =
    numbers.partition { it > 0 }

positive == listOf(1, 3, 2)
negative == listOf(-4, -11)
[/expander_maker] [expander_maker id=”1″ more=”答え” less=”非表示”]

答え

//未配送の商品の方が多い顧客を返す
fun Shop.getCustomersWithMoreUndeliveredOrders(): Set<Customer> = 
    customers.filter {
        val (delivered, undelivered) = it.orders.partition { it.isDelivered }
        undelivered.size > delivered.size
    }.toSet()

[/expander_maker]

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

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

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

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

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

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

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]

groupBy()でグルーピングする

Kotlinの標準ライブラリにはコレクションの要素をグルーピングするための拡張関数が用意されています。基本的な関数はgroupBy()で、ラムダ関数を引数にとり、Mapを返します。

返されたMapではそれぞれのキーはラムダ関数の結果で、値にはそのラムダ関数の結果に対応する要素のリストが入ります。使用例としては、文字列を最初の文字ごとにグルーピングしたりする際などに使われます。

またgroupBy()関数は2つ目の引数に変換関数であるラムダ関数を設定して呼び出すこともできます。この場合の返り値のMapでは、keySelector関数によって生成された値がキーに、変換関数の結果が値にセットされます。

例)

val numbers = listOf("one", "two", "three", "four", "five")

println(numbers.groupBy { it.first().uppercase() })
//{O=[one], T=[two, three], F=[four, five]}
println(numbers.groupBy(keySelector = { it.first() }, valueTransform = { it.uppercase() }))
//{o=[ONE], t=[TWO, THREE], f=[FOUR, FIVE]}

問題

groupByを使ってcustomerをcityごとに分類したリストを格納するマップを返す関数を実装してください。

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

問題コード:

fun Shop.groupCustomersByCity(): Map<City, List<Customer>> =
        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=”非表示”]

答え

// Build a map that stores the customers living in a given city
fun Shop.groupCustomersByCity(): Map<City, List<Customer>> =
        customers.groupBy { it.city }

[解説]

引数にit.cityを指定することでcustomers中のそれぞれのcustomerのcityプロパティで分類しています。

[/expander_maker]

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

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

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

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

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

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

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]

Association変換でMapを作成する

Association(=関連)変換を使うとコレクションの要素と、その要素に関連付けされた特定の値から新たにMapを作成することができます。

Association変換にはいくつか種類があり、使い分けることで要素自体をキーにすることも値にすることもできます。具体的にはassociateWith()associateBy()associate()の3つがあります。それぞれ確認していきましょう。

associateWith()

一番基本の関数はassociateWith()です。これはもとのコレクションの要素がキーとなり、引数に与えられた変換関数によって生成された値がMapの値になります。等しい要素がある場合、最後の要素がMapに残存します。

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

上記コードでは引数に it.length を渡しているので、”one”, “two”, “three”, “four”それぞれの文字列の長さが値になるMapを返します。

associateBy()

もとのコレクションの要素を値とするMapを作るためにはassociateBy()を使用します。

これは要素の値に基づいたキーを返す関数を引数にとり、Mapを作成します。等しい要素がある場合、最後の要素がMapに残存します。

val numbers = listOf("one", "two", "three", "four")
println(numbers.associateBy { it.first().uppercaseChar() })
//{O=one, T=three, F=four}

引数に渡されたit.first().uppercaseChar()は、it.first()でそれぞれの要素の最初の一文字を指定し、uppercaseChar()でそれらを大文字に変換しています。

出力結果ではもとのリストの要素が値になっていることがわかります。

associate()

Another way to build maps in which both keys and values are somehow produced from collection elements is the function associate(). It takes a lambda function that returns a Pair: the key and the value of the corresponding map entry.

キーと値がの両方がなんらかの形でもとのコレクションの要素から生成される場合はassociate()を使います。これは対応するMapのエントリのキーと値のPairを返すラムダ関数を引数にとります。

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}

associate()ではこのためだけにPairオブジェクトが生成されるので、パフォーマンスに影響を及ぼす可能性があります。従ってパフォーマンスがそこまで重要視されていないときや、先に説明した二つの関数が使えない場合にのみ、associate()を使うことが推奨されています。

上記コードは少し複雑なので、より分かりやすく使っている例が以下になります。

val list = listOf("abc", "cdef")
print(list.associate { it.first() to it.length } )
//{a=3, c=4}

{キー to 値}の形で指定することができます。

問題

associateBy()、associateWith()、associate()を用いて以下の関数を実装してください。

  1. customerの名前がキー、customerが値のマップを返すnameToCustomerMap()
  2. customerがキー、customerのcityが値のマップを返すcustomerToCityMap()
  3. customerの名前がキー、customerのcityが値のマップを返すcustomerNameToCityMap()

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

問題コード:

//customerの名前がキー、customerが値のマップを返す
fun Shop.nameToCustomerMap(): Map<String, Customer> =
        TODO()

//customerがキー、customerのcityが値のマップを返す
fun Shop.customerToCityMap(): Map<Customer, City> =
        TODO()

//customerの名前がキー、customerの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
}
[expander_maker id=”1″ more=”答え” less=”非表示”]

答え

//customerの名前がキー、customerが値のマップを返す
fun Shop.nameToCustomerMap(): Map<String, Customer> =
        customers.associateBy { it.name }

//customerがキー、customerのcityが値のマップを返す
fun Shop.customerToCityMap(): Map<Customer, City> =
        customers.associateWith { it.city }

//customerの名前がキー、customerのcityが値のマップを返す
fun Shop.customerNameToCityMap(): Map<String, City> =
    	customers.associate { it.name to it.city }

[解説]

nameToCutomerMap()はキーが元のリストとなるcustomersから生成されたnameなので、associateBy()を使います。

逆にcustomerToCityMap()ではキーがcustomersの要素であるcustomerなので、associateWith()を使います。

customerNameToCityMap()ではキーも値もcustomersから生成されたものを設定したいので、associateを使います。

別解として、customers.associateBy(Customer::name)やcustomers.associateWith(Customer::city)の形にして指定することもできます。

[/expander_maker]

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

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

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

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

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

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

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]

sumByによって指定した式の合計を求める

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

上の記事でも解説した通り、リストの要素の合計はsum()関数で取得することができます。

listOf(1, 5, 3).sum() == 9

下のコードはリストに含まれる要素の文字列の文字数の合計を求めています。

listOf("a", "b", "cc").sumBy { it.length } == 4

このように、要素自身が数値なのではなく、文字の長さなど要素のある性質を数値化して合計処理をさせたい場合はsumBy()関数を使って、ラムダ式を渡すことで求めることができます。

Double型で返したいときには同じようにしてsumByDouble()を使うことができます。

問題

引数に渡した顧客によって注文された商品の合計金額を計算させて、顧客が使った合計金額を求める関数を実装してください。ただしそれぞれの商品は注文される度にカウントされるものとします。

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

問題コード:

// 引数に渡した顧客によって注文された商品の合計金額を返す
fun moneySpentBy(customer: Customer): Double =
        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=”非表示”]

答え

// 引数に渡した顧客によって注文された商品の合計金額を返す
fun moneySpentBy(customer: Customer): Double =
    customer.orders.flatMap { it.products }.sumByDouble { it.price }

[解説]

今回はDouble型を返したいのでsumByDoubleを使っています。

順序として、引数に渡されたcustomerからordersを取り出し、それにflatMapを使ってネストの階層を平坦にしています。flatMapの実行結果の例を以下に載せておきます

val list = listOf(listOf("a", "b"), listOf("c", "d"), listOf("e", "f"))

list.flatMap { it } //[a, b, c, d, e, f]

その後、flatMapで平坦にしたproductのpriceの合計を求める処理をsumByDoubleで行っています。

[/expander_maker]

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

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

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

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

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

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

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]

コレクションの集計操作

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
}
[expander_maker id=”1″ more=”ヒント” less=”非表示”]

ヒント

ラムダ式の代わりに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)

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

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

答え

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

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

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

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

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

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

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

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

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]

predicateの種類

以下の記事で説明したように、Kotlinではコレクションから特定の条件でデータを抽出する際の条件はpredicateというラムダ式によって定義されます。

【Kotlin練習問題】コレクション:フィルタリング

predicateと組み合わせて使われる関数にはいくつかタイプがあり、filter()のように条件に合致する要素を返すフィルタリング用の関数の他に今回紹介する、要素がpredicateに合致しているかどうかをtrueかfalseで返すtest predicateとよばれるものがあります。

test predicateにはall()any()none()の三つがあります。それぞれの使い方を以下で説明していきます。

all()の使い方

all()は渡されたpredicateに全ての要素が合致していればtrueを返します。

空のコレクションでall()が呼び出された場合はtrueが返されます。

以下のコードをご覧ください。

val numbers = listOf("one", "two", "three", "four")

println(numbers.all { it.endsWith("e") }) //false

このコード中のpredicateはit.endWith(“e”)の部分ですが、関数名からも推測できるように要素が”e”で終わっているかを確認するためのものです。

numbersの中で”e”で終わっている要素は”one”と”three”だけなので、結果はfalseが返されます。

any()の使い方

 any()もall()と使い方は同じです。

any()の場合は渡されたpredicateに一つでも要素が合致していればtureが返されます。

先ほどと同じコードでall()との違いを確認してみましょう。

val numbers = listOf("one", "two", "three", "four")

println(numbers.any { it.endsWith("e") }) //true
println(numbers.all { it.endsWith("e") }) //false

any()の場合は一つでも合致していればtrueが返されるので、”one”と”three”があてはまり、trueが返されています。

none()の使い方

none()もall()、any()と使い方は全く同じです。

none()は渡されたpredicateに要素が一つも合致していなければtrueが返されます。

val numbers = listOf("one", "two", "three", "four")

println(numbers.none { it.endsWith("a") }) //true

今回は”a”で終わる要素を探すようにpredicateを指定していますが、”a”で終わる要素は一つもないのでtrueが返されます。

またany()とnone()に関してはpredicateがなくても使うことができます。

その場合、コレクションが空かどうかをチェックしてくれます。
any()はそのコレクションに一つでも要素があればtrue、空であればfalseを返します。

none()はany()と逆の挙動をします。

問題

all、anyを活用して以下の関数を実装してください。

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

問題コード:

// 全てのcustomerが引数に与えられたcity出身であればtrueを返す
fun Shop.checkAllCustomersAreFrom(city: City): Boolean =
        TODO()

// 与えられたcity出身のcustomerが一人でもいればtrueを返す
fun Shop.hasCustomerFrom(city: City): Boolean =
        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=”非表示”]

答え

// 全てのcustomerが引数に与えられたcity出身であればtrueを返す
fun Shop.checkAllCustomersAreFrom(city: City): Boolean =
        customers.all { it.city == city }

// 与えられたcity出身のcustomerが一人でもいればtrueを返す
fun Shop.hasCustomerFrom(city: City): Boolean =
        customers.any { it.city == city }

[解説]

customerについて調べたいので、allとanyが使われるのはShopクラスの中にあるcustomersに対してです。

predicateではit.cityでそれぞれの要素(customer)のcityと引数に与えれたcityが等しいかを調べています。

allもanyも渡されるpredicateはまるっきり同じです。

[/expander_maker]

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

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

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

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

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

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

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]

コレクションのフィルタリング

あるコレクションの中から特定の条件を満たす(または満たさない)要素だけを抽出する処理のことをフィルタリングと言います。

フィルタリングはコレクションの処理において最もメジャーな処理とも言えます。

Kotlinではフィルタリングする際の条件はpredicate(プレディケイト:述部の意)によって定義されます。predicateはコレクションの要素を取り、boolean値を返すラムダ関数のことです。

predicateに合致していればtrueそうでなければfalseが返されるようになっています。

標準ライブラリには一回の呼び出しでコレクションをフィルタリングできる拡張関数がいくつか用意されています。

これらの関数はもとのコレクションには変更を加えないので、そのコレクションが読み込み専用かそうでないかは問わずに利用することができます。

フィルタリングした結果を処理するには結果を変数に代入するか、フィルタリングの後でチェーンにして別の関数で処理します。

predicateでフィルタリングする

基本的なフィルタリング用の関数はfilter()です。predicateを使ってfilter()を呼び出すと、それに合致する要素を返します。

またListに対してもSetに対しても返される結果のコレクションはListです。Mapに対してはMapが返されます。

val numbers = listOf("one", "two", "three", "four")  
val longerThan3 = numbers.filter { it.length > 3 } //要素が3より大きい
println(longerThan3) //[three, four]

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10} //キーが"1"
で終わっており値が10よりも大きい
println(filteredMap) //{key11=11}

上記コードの numbers.filter { it.length > 3 }におけるit.length > 3や、numbersMap.filter { (key, value) -> key.endsWith(“1”) && value > 10}における(key, value) -> key.endsWith(“1”) && value > 10predicateに該当します。


it.length のitはnumbersに含まれるそれぞれの要素を意味します。

基本的なフィルタリング方法は以上になります。それでは確認問題を解いてみましょう。

問題

filter()を用いて指定されたcityに住む顧客(customer)のリストを返すメソッドを実装してください。

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

問題コード:

// 指定したcityに住む顧客を取得する
fun Shop.getCustomersFrom(city: City): List<Customer> =
        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=”非表示”]

答え

fun Shop.getCustomersFrom(city: City): List<Customer> =
        customers.filter { it.city == city }

[解説]

getCustomersFrom関数に渡されたcityとcustomersに含まれる要素のcityが合致するかを調べるpredicateを指定しています。

[/expander_maker]

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

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

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

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

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

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

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]

マップ(Map)とは

Kotlinスタンダードライブラリにはコレクションを変換するための拡張関数のセットが用意されています。これらの関数は指定された変換ルールに従って既存のコレクションから新しいコレクションを生成します。このページではその一つであるマップ(Map)について解説していきます。


マッピング変換ではあるコレクションの要素に対して使われた関数の結果から新しいコレクションが生成されます。基本的なマッピング関数はmap()です。

map()は与えられたラムダ関数を後続の要素に適用し、そのラムダの結果のリストを返します。

結果の順番はもともとの要素の順番と同じです。引数として要素のインデックス番号を使う変換を適用する場合は、mapIndexed()を使います。使用例を見てみましょう。

val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 }) //[3, 6, 9]
println(numbers.mapIndexed { idx, value -> value * idx }) //[0, 2, 6]

上記コードでは it * 3の部分とidx, value -> value * idxの部分がラムダ関数で、itはnumbersに含まれるそれぞれの要素を意味します。

またidxとvalueはそれぞれインデックス番号とその値(要素)を意味しており、一つ目はインデックス番号が0,値が1なので、返される値は0 * 1(=0)になります。同様に1 * 2、2 * 3と続きます。

もし変換の結果、nullが要素に含まれる場合、map()の代わりにmapNotNull()関数を使うことで、返されるコレクションからnullを取り除くことができます。mapIndexed()に対してはmapIndexedNotNull()が使えます。

val numbers = setOf(1, 2, 3)
println(numbers.mapNotNull { if ( it == 2) null else it * 3 }) //[3, 9]
println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx }) //[2, 6]

mapを変換する場合、2つの方法があります。一つは値を変更せずにキーのみを変換する方法と、キーを変更せずに値のみを変換する方法です。

与えられた変換ルールをキーに適用したい場合はmapKeys()を使います。値の場合はmapValues()になります。

これらの関数は両方ともmapをマップエントリー(Mapに保持されたキーと値のペアのこと)を引数にとる変換を使っているので、キーと値の両方とも処理することができます。

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
println(numbersMap.mapKeys { it.key.uppercase() }) //{KEY1=1, KEY2=2, KEY3=3, KEY11=11}
println(numbersMap.mapValues { it.value + it.key.length }) //{key1=5, key2=6, key3=7, key11=16}

いかがでしたか?今回はマッピング変換について簡単に解説しました。
最後に確認問題を解いてみましょう。

問題

map()を用いて全ての顧客(customer)の出身地(city)を取得する拡張関数を実装してください。

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

問題コード:

//顧客の出身地を取得する
fun Shop.getCustomerCities(): Set<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
}
[expander_maker id=”1″ more=”答え” less=”非表示”]

答え

fun Shop.getCustomerCities(): Set<City> =
        customers.map {it.city}.toSet()

[解説]

mapを用いてShopに含まれるcustomers(List型)をcityのリストに変換しています。ただし、返り値はSet型にしなければいけないので、最後にtoSet()をつけてListからSetに変換するのを忘れないようにしましょう。

[/expander_maker]

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

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

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

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

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

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

おすすめ書籍

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

[itemlink post_id=”1743″]

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

[itemlink post_id=”1745″]