fold() into a set Koan is not accepted - kotlin

I am learning Kotlin via Koans here: https://play.kotlinlang.org/koans/Collections/Fold/Task.kt
this is my code so far:
// Return the set of products that were ordered by all customers
fun Shop.getProductsOrderedByAll(): Set<Product> {
val orderedProductsByAll = this.customers.fold(mutableSetOf<Product>()) {
collector, c -> collector.plus(c.getOrderedProducts()).toMutableSet()
}
return orderedProductsByAll
}
fun Customer.getOrderedProducts(): List<Product> =
this.orders.flatMap { o -> o.products }
Unfortunately the results is not accepted:
Fail: testGetProductsOrderedByAllCustomers: The function 'getProductsOrderedByAll' is implemented incorrectly
Any tips on what I am doing wrong here?

You have probably misunderstood the question...
Return the set of products that were ordered by all customers
means
Give me the products that are been purchased by all customer
therefore, you don't want to return "all the products that are been purchased at least 1 time by any customer", but only those that every customer have bought at least one time
At the end, "mathematically speaking" you don't want an "union", but an "intersection"... and the collector has infact an intersect method...
OT note:
also, consider that your plus method is not doing anything to the set, and your code is equivalent to:
return customers.flatMap { c -> c.getOrderedProducts() }.toSet()

Related

mapping custom object kotlin

I have a custom object:
data class MoneyTransaction(
val amount: Double,
val category: String
)
I have a list of MoneyTransaction. I want to create a map out of that list where keys are categories, and the values are the total amount according to the category. Kotlin has functions like groupBy, groupByTo, groupingBy. But there is no tutorial or documentation about those, so I can't figure it out. So far I got this:
val map = transactionList.groupBy({it.category},{it.amount})
But this doesn't give the total amount, just separate amounts on each category
Any help would be much appreciated.
So first of all you group your transactions by category
transactionList.groupBy { it.category }
this gives you a Map<String, List<MoneyTransaction>> after that you need to sum up the amounts
transactionList.groupBy { it.category }
.mapValues { (_, transactionsInCategory) ->
transactionsInCategory.sumOf { it.amount }
}
This will give you a Map<String, Double> with the value representing the sum of all transactions in the category.
You can use groupingBy and then fold:
transactions.groupingBy(MoneyTransaction::category)
.fold(0.0) { acc, next -> acc + next.amount }
groupingBy here would return a Grouping<MoneyTransaction, String>, which is an intermediate collection of the groups. Then you fold each of the groups by starting from 0, and adding the next transaction's amount.
Looking at the implementation, the groupingBy call doesn't actually does any actual "grouping" - it just creates a lazy Grouping object. So effectively, you are going through the collection only once.

How to create filters in Spring boot based on a List

I have to created filters data in spring boot. From frontEnd, I am sending a list containing the selected id of each category.I need to return items based on this list. I can do it this way
Service:
public List<ProductModel> getProductFilter(Integer[] categories) {
int size = categories.length;
if(size == 1){
return productRepository.getProductFilteredByOneCategory(Long.valueOf(categories[0]));
}else {
return productRepository.getProductFilteredByTwoCategory(Long.valueOf(categories[0]),Long.valueOf(categories[1]));
}
}
}
Repository:
#Query("SELECT c FROM ProductModel c WHERE c.categoryModel.id = :Category_id")
List<ProductModel> getProductFilteredByOneCategory(Long Category_id);
#Query("SELECT c FROM ProductModel c WHERE c.categoryModel.id = :Category_idOne OR c.categoryModel.id = :Category_idTwo")
List<ProductModel> getProductFilteredByTwoCategory(Long Category_idOne, Long Category_idTwo);
But if there are 50 of these categories, it is useless. Can anyone tell me how to make it better? There is some way to generate a query from a list?
You can use in instead of using multiple or operations as follows. It select all productModels match any categoryModel id in List.
#Query("SELECT c FROM ProductModel c WHERE c.categoryModel.id in category_ids")
List<ProductModel> getProductFilteredByCategoryIds(List<Long> Category_ids);
As #YJR said, IN clause is the solution, but you should also consider declaring query method as shown below, which doesn't require writing JPQL.
public List<ProductModel> findByCategoryModel_IdIn(List<Long> categoryIds);

Feedback on Lambdas

I was hoping someone could provide me some feedback on better/cleaner ways to do the following:
val driversToIncome = trips
.map { trip ->
// associate every driver to a cost (NOT UNIQUE)
trip.driver to trip.cost }
.groupBy (
// aggregate all costs that belong to a driver
keySelector = { (driver, _) -> driver },
valueTransform = { (_, cost) -> cost }
)
.map { (driver, costs) ->
// sum all costs for each driver
driver to costs.sum() }
.toMap()
You can do it like this:
val driversToIncome = trips
.groupingBy { it.driver }
.fold(0) { acc, trip -> acc + trip.cost }
It groups trips by driver and while grouping it sums costs per each driver separately.
Note that groupingBy() does not do anything on its own, it only prepares for the grouping operation. This solution avoids creating intermediary collections, it does everything in a single loop.
Then fold() calls the provided lambda sequentially on each item belonging to the specific group. Lambda receives a result from the previous call and it provides a new result (result is called accumulator). As a result, it reduces a collection of items to a single item.
You can read more about this kind of transformations in documentation about Grouping and Aggregation. Also, they aren't really inventions of Kotlin. Such operations exist in other languages and data transformation tools, so you can read about it even on Wikipedia.

How to get documents from Firestore where a field which is an array matches a string in Kotlin?

I want to get all documents (products) from the Firestore and then show them in the recyclerVieww. The criterion is that the value that I have should match with the one in the 'deliverable', where the deliverable is an array.
So, get all the documents from the collection 'products' where the value in the field 'deliverable match the one I provide.
I have tried the following but I don't get any result as I expected. Am I doing it wrong?
Can someone help me with it?
fun getProductsDeliverable(deliverable: String) {
mFireStore.collection("products")
.whereEqualTo("deliverable_mun_pan", deliverable).get().addOnCompleteListener {
if (it.isSuccessful) {
for (document in it.result) {
val product = document.toObject(Product::class.java)
product.product_id = document.id
productsList.add(product)
}
} else {
Log.d("TAG", "There are no items")
}
srProductsList.addAll(srProductsList)
successProductsList(srProductsList)
}
What you're describing is known as an array membership query in Firestore, and can be done with
mFireStore.collection("products")
.whereArrayContains("deliverable", "Pune")
So this matches products where the deliverable array contains a value of "Pune". Instead of the hard-coded value, you can also specify your deliverable variable of course.

How can I translate this Kotlin code into a better one using high order functions instead of a simple for

I have this function that receives a barcode and looks for a product in a list that has the same barcode. The split( ",") is because there are some products that have more than one barcode written like this: ("barcode1,barcode2")
Could someone help me get a better code using high order functions rather than this for loop?
fun Product.byBarcode(barcode: String?) : Product? {
val productsList = Realm.getDefaultInstance().where(Product::class.java).findAll().toMutableList()
var foundProduct : Product? = null
for (it in productsList){
if ( it.barcode.split(",").contains(barcode)){
foundProduct = it
break
}
}
return foundProduct
}
You can use find
foundProduct = productList.find{ it.barcode.split(',').contains(barcode) }
also I don't think split is really required, in that case
foundProduct = productList.find{ it.barcode.contains(barcode) }