This portion of the Kotlin Koans tutorial appeared to be a review of the concepts I had been working on throughout the collection section. I had to solve three different problems using the collections API. While doing this, I got to revist the Elivis operator (?:), map, maxBy, sumBy, filter, count, and toSet.
Get Customers Who Ordered Product
This problem focused on filtering.
fun Shop.getCustomersWhoOrderedProduct(product: Product): Set { // Return the set of customers who ordered the specified product return customers.filter { it.orderedProducts.contains(product) }.toSet() }
The filter method takes a predicate that returns true or false. In this case, I just used the contains method on orderedProducts. If the product is found in orderedProducts, we get a true, otherwise false. Then there is a toSet() operation to transform the collection to a set.
Get Most Expensive Delived Products
This problem was a little more challenging. I had to go back and review how to use the Elivis operator (TODO: Link).
fun Customer.getMostExpensiveDeliveredProduct(): Product? { // Return the most expensive product among all delivered products // (use the Order.isDelivered flag) return orders.filter { it.isDelivered }.map { it.products.maxBy { it.price } }.maxBy { it?.price ?: 0.0} }
I started with a filter operation to check if an order was delivered or not since the problem statement required me to find the most expensive delivered product. Then I had to use a map operation which allowed me to traverse all delivered orders. At this point, I could use a maxBy operation and check it.price. This builds up a collection of products that contains the most expensive product on each order.
The next part of the operation is to find the most expensive product of all orders. At this point, I have a collection of products so I just needed another maxBy operation. However it was a little more trickey this time. In this case, there was a possibily that the variable it
could be null. It’s nice that Kotlin has compiler checks for this sort of thing because I truthfully didn’t realize that I could be working with null objects here. Thus, I had to use the Elvis operator in this final lambda operation.
Get Number Of Times Product Was Ordered
I had to solve this problem by chaining transformations together again.
fun Shop.getNumberOfTimesProductWasOrdered(product: Product): Int { // Return the number of times the given product was ordered. // Note: a customer may order the same product for several times. return customers.sumBy { it.orders.sumBy { it.products.count { it == product } } } }
A customer has a one to many relationship with orders, and orders have a one to many relationship with products. I needed two sumBy operations to solve this problem. I began with a sumBy on customers. Inside of the lambda, I did another sumBy operation on orders. Once I was traversing orders, I could do a count operation on products and get a total of how many products matched my predicate.
The it.products.count returns a number that gets fed into it.orders.sumBy. The it.orders.sumBy returns a number that gets fed into customers.sumBy. Once customers.sumBy returns, we have a count of the total number of times the specified product was ordered.
You can click here to see Part 22