How to deconstruct map to list? - kotlin

I have a List that contain country, I used groupBy to group them by country and now i would like to get fallowing result (for recycled view):
Country1
Airport1
Airport2
Airport3
Country2
Airport4
Airport5
Airport6
could someone give me a tip on how to do this?
My data class:
data class Airport(
val city: City,
val code: String,
val country: Country,
val name: String,
)

Assuming:
after you call groupBy you have a Map<Country, List<Airport>>
you want the countries to be in alphabetical order
you want to transform the map to a List<String>
This should do it:
airportsByCountry
.toSortedMap()
.flatMap { (country, airports) ->
listOf(country.toString()) + airports.map { it.toString() }
}

Related

Aliasing count() for several columns in a group by query in Exposed

I'm trying to create a query like the presented one in the ORM Exposed.
select t.a, t.b, count(*)
from table as t
group by t.a, t.b;
But it seems that .count() supports aliases only for one column, but here I need two columns:
val count = Table.Table.<somehow I need to push both fields a and b here>.count()
Table.Table
.slice(Table.Table.a, Table.Table.b, count)
.selectAll()
.groupBy(Table.Table.a, Table.Table.b)
.map { row ->
Result(
state = row[Table.Table.a],
trigger = row[Table.Table.b],
count = row[count]
)
}
Are you have any ideas on how to do it?
val count = Count(stringLiteral("*"))
This will generate COUNT('*') in your query (which is a valid SQL expression, giving the same result as COUNT(*)).
If you want to get rid of these annoying quotes, you may do the following:
val asterisk = object : Expression<String>() {
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
queryBuilder { +"*" }
}
}
val count = Count(asterisk)

Compare same value in 2 different lists - Kotlin

I'm very new to kotlin and I'm doing this exercise where you need 2 lists, one for the invoice and another for the invoice values, for example:
Invoice..........................│ Invoice Values
invoiceNumber = 1........│ itemName = Pepsi
customerName = Alfred│ quantity = 2
TotalValue = $13,00......│ price = 2.50
......................................│ invoiceNumber = 1
......................................│
......................................│ itemName = Cereal
......................................│ quantity = 2
......................................│ price = 4
......................................│ invoiceNumber = 1
and I got it to show only the invoiceNumber that I want, but I can't manage to get the results to do "total += quantity*price" here is what I tried so far:
fun main() {
data class VarInvoice(val numInvoice: Int, val dateInvoice: String, val ssn: String, val total: Double)
data class VarItem(val nameItem: String, val quantity: Int, val unitPrice: Double, val numInvoice2: Int)
val invoice= mutableListOf(
VarInvoice(1, "25/05/1990", "84739572857", 0.00),
VarInvoice(2, "02/09/2009", "38295840284", 0.00),
VarInvoice(3, "13/07/2020", "74959572857", 0.00)
)
val invoiceItem = mutableListOf(
VarItem("Pepsi", 2, 2.50, 1),
VarItem("Cereal", 2, 4.00, 1),
VarItem("Coke", 4, 3.5, 2),
VarItem("Chicken", 1, 13.50, 3)
)
val itemInvoice = invoiceItem.groupBy(VarItem::numInvoice2)
val invoiceEqual = itemInvoice.getValue(1)
println(invoiceEqual)
//but this returns me: [VarItem(nameItem=Pepsi, quantity=2, unitPrice=2.5, numInvoice2=1), VarItem(nameItem=Cereal, quantity=2, unitPrice=4.0, numInvoice2=1)]
//and i have no idea how to do like: if invoiceNumber2 == invoiceNumber { (total += price * quantity)}
Map to total
Assuming you are trying to get the total of a invoice, here is something you could do:
val totals = invoiceItem.groupBy { it.numInvoice2 }.mapValues { item ->
item.value.sumByDouble { it.unitPrice * it.quantity }
}
The totals is a map of invoice number to the total price of all associated items. (assuming you are trying to join invoice to invoiceItem on invoiceNumber2 = invoiceNumber
Extension Property
Or you can slap an extension property onto the VarInvoice, instead of declaring it on the data class as a property (just beware that you can't do this in a function (e.g. your main function))
val VarInvoice.total get() = invoiceItem
.filter { it.numInvoice2 == numInvoice }
.sumByDouble { it.unitPrice * it.quantity }
It would be nice you can provide more information on how you are going to build your application. There could be better solution to this.
P.S. 1
after your code, I got: {1=13.0, 2=14.0, 3=13.5} and I'm trying to figure a way of it beeing sorted and to include the ssn of the person, like this: {1=13.0 "by person with ssn: "84739572857, 3=13.5 "by person with ssn: "74959572857, 2=14.0 "by person with ssn: "38295840284 Sorry, english is not my main language and I can't thank you enough!
Technically you can try something like this:
val totaledInvoice = invoiceItem.groupBy { it.numInvoice2 }.mapValues { item ->
invoice.first { it.numInvoice == item.value.first().numInvoice2 }.copy(
total = item.value.sumByDouble { it.unitPrice * it.quantity }
)
}
You can return whatever you want in the mapValues lambda, but the code is getting ridiculous and inefficient. I would suggest trying out the extension property solution.

How to sort list of objects by two properties and collator

I have to sort list of object by lastName and firstName (if case there is the same lastName for multiple objects). I must also apply Collator to those.
Suppose I can do that for one property:
val collator = Collator.getInstance(context.getResources().getConfiguration().locale)
myList.sortedWith(compareBy(collator, { it.lastName.toLowerCase() }))
But is it possible to apply also to that another restriction to also sort by firstName?
You can add another sort criteria with thenBy:
val comparator =
compareBy(collator) { p: Person -> p.lastName.toLowerCase() }
.thenBy(collator) { p: Person -> p.firstName.toLowerCase() }
val result = myList.sortedWith(comparator)
Simplest would just be to concatenate the two properties in the selector lambda:
myList.sortedWith(
compareBy(collator) { "${it.lastName} ${it.firstName}".toLowerCase() }
)

How correctly built an object graph based on multi level join in Slick?

I have a model structure as following:
Group -> Many Parties -> Many Participants
In on of the API calls I need to get single groups with parties and it's participants attached.
This whole structure is built on 4 tables:
group
party
party_participant
participant
Naturally, with SQL it's a pretty straight forward join that combines all of them. And this is exactly what I am trying to do with slick.
Mu method is dao class looks something like this:
def findOneByKeyAndAccountIdWithPartiesAndParticipants(key: UUID, accountId: Int): Future[Option[JourneyGroup]] = {
val joins = JourneyGroups.groups join
Parties.parties on (_.id === _.journeyGroupId) joinLeft
PartiesParticipants.relations on (_._2.id === _.partyId) joinLeft
Participants.participants on (_._2.map(_.participantId) === _.id)
val query = joins.filter(_._1._1._1.accountId === accountId).filter(_._1._1._1.key === key)
val q = for {
(((journeyGroup, party), partyParticipant), participant) <- query
} yield (journeyGroup, party, participant)
val result = db.run(q.result)
result ????
}
The problem here, is that the result is type of Future[Seq[(JourneyGroup, Party, Participant)]]
However, what I really need is Future[Option[JourneyGroup]]
Note: case classes of JourneyGroup and Party have sequences for there children defined:
case class Party(id: Option[Int] = None,
partyType: Parties.Type.Value,
journeyGroupId: Int,
accountId: Int,
participants: Seq[Participant] = Seq.empty[Participant])
and
case class JourneyGroup(id: Option[Int] = None,
key: UUID,
name: String,
data: Option[JsValue],
accountId: Int,
parties: Seq[Party] = Seq.empty[Party])
So they both can hold the descendants.
What is the correct way to convert to the result I need? Or am I completely in a wrong direction?
Also, is this statement is correct:
Participants.participants on (_._2.map(_.participantId) === _.id) ?
I ended up doing something like this:
journeyGroupDao.findOneByKeyAndAccountIdWithPartiesAndParticipants(key, account.id.get) map { data =>
val groupedByJourneyGroup = data.groupBy(_._1)
groupedByJourneyGroup.map { case (group, rows) =>
val parties = rows.map(_._2).distinct map { party =>
val participants = rows.filter(r => r._2.id == party.id).flatMap(_._3)
party.copy(participants = participants)
}
group.copy(parties = parties)
}.headOption
}
where DAO method's signature is:
def findOneByKeyAndAccountIdWithPartiesAndParticipants(key: UUID, accountId: Int): Future[Seq[(JourneyGroup, Party, Option[Participant])]]

scala: how to model a basic parent-child relation

I have a Brand class that has several products
And in the product class I want to have a reference to the brand, like this:
case class Brand(val name:String, val products: List[Product])
case class Product(val name: String, val brand: Brand)
How can I poulate these classes???
I mean, I can't create a product unless I have a brand
And I can't create the brand unless I have a list of Products (because Brand.products is a val)
What would be the best way to model this kind of relation?
I would question why you are repeating the information, by saying which products relate to which brand in both the List and in each Product.
Still, you can do it:
class Brand(val name: String, ps: => List[Product]) {
lazy val products = ps
override def toString = "Brand("+name+", "+products+")"
}
class Product(val name: String, b: => Brand) {
lazy val brand = b
override def toString = "Product("+name+", "+brand.name+")"
}
lazy val p1: Product = new Product("fish", birdseye)
lazy val p2: Product = new Product("peas", birdseye)
lazy val birdseye = new Brand("BirdsEye", List(p1, p2))
println(birdseye)
//Brand(BirdsEye, List(Product(fish, BirdsEye), Product(peas, BirdsEye)))
By-name params don't seem to be allowed for case classes unfortunately.
See also this similar question: Instantiating immutable paired objects
Since your question is about model to this relationship, I will say why not just model them like what we do in database? Separate the entity and the relationship.
val productsOfBrand: Map[Brand, List[Product]] = {
// Initial your brand to products mapping here, using var
// or mutable map to construct the relation is fine, since
// it is limit to this scope, and transparent to the outside
// world
}
case class Brand(val name:String){
def products = productsOfBrand.get(this).getOrElse(Nil)
}
case class Product(val name: String, val brand: Brand) // If you really need that brand reference