I have following classes:
class UserData(
val firstSale: Date,
val lastSale: Date,
val ptuCodesList: List<ItemSale>,
val taxList: List<ItemSale>,
val salesList: List<ItemSale>,
val taxTotal: Long,
val saleTotal: Long,
val currency: String = DEAFULT_CURRENCY,
val canceledOrderCount: Int,
val canceledSaleTotal: Long,
)
And model class:
class ItemSale(
val itemDescription: String,
val itemsTotalValue: Long
)
I'm fetchig all my UserData objects from my db:
dbRepository.getAllUsersData(): List<UserData>
And now i dont know how to sum this List UserData objects and return as single object i mean, i have no idea how to sum those lists taxList/saleList included in UserData object. In those lists i need to sum itemsTotalValue.
Example:
We have list of two UserData objects:
//1
class UserData(
val firstSale: 238402384092L,
val lastSale: 893231117482L,
val ptuCodesList: List<ItemSale>,
val taxList: List<ItemSale>, // ("first", 4), ("second", 4)
val salesList: List<ItemSale>, // ("third", 5), ("fourth", 3)
val taxTotal: 500,
val saleTotal: 600,
val currency: String = DEAFULT_CURRENCY,
val canceledOrderCount: 3,
val canceledSaleTotal: 4500
)
//2
class UserData(
val firstSale: 338402384092L,
val lastSale: 9934798237482L,
val ptuCodesList: List<ItemSale>,
val taxList: List<ItemSale>, // ("first", 7), ("second", 8)
val salesList: List<ItemSale>, // ("third", 1), ("fourth", 2)
val taxTotal: 250,
val saleTotal: 400,
val currency: String = DEAFULT_CURRENCY,
val canceledOrderCount: 4,
val canceledSaleTotal: 400
)
We need to sum them like this:
//Result 1 2
class UserData(
val firstSale: 238402384092L,
val lastSale: 9934798237482L,
val ptuCodesList: List<ItemSale>, //Stay same for all objects
val taxList: List<ItemSale>, // ("first", 11), ("second", 12)
val salesList: List<ItemSale>, // ("third", 6), ("fourth", 5)
val taxTotal: 750,
val saleTotal: 1000,
val currency: String = DEAFULT_CURRENCY, // Stay for all same
val canceledOrderCount: 7,
val canceledSaleTotal: 4900
)
CodePudding user response:
Welcome Jumanji,
You need to do this step by step, i.e. separately calculate each field you need to calculate and at the end just create an instance of UserData using those calculated fields.
Getting more into details - you can create helper method to concatenate ("sum") two lists of List<ItemSale>
which could look like this:
private fun concatenateItemSales(sales1: List<ItemSale>, sales2: List<ItemSale>): List<ItemSale> {
val itemsFromSales1ConcatenatedWithSales2: List<ItemSale> = sales1.map { item1 ->
val item2Value: Long? = sales2.find { it.itemDescription == item1.itemDescription }?.itemsTotalValue
val itemsTotalValue = item1.itemsTotalValue (item2Value ?: 0L)
ItemSale(item1.itemDescription, itemsTotalValue)
}
val sales1ItemDescriptions: List<String> = sales1.map { it.itemDescription }
val itemsFromSales2NotInSales1: List<ItemSale> = sales2.filter { it.itemDescription !in sales1ItemDescriptions }
return itemsFromSales1ConcatenatedWithSales2 itemsFromSales2NotInSales1
}
This function assumes possible occurence of items that are present only in one of these two lists - for safety reasons. If you're sure there always be exactly same items (with same descriptions) and there will no such differences - you can always simplify this code. You can also improve code this function to use functional style like one presented by broot in the another answer. I focused more on idea than on providing best possible code.
Having this function it should be easy for you to create concatenated UserData
, e.g. this way:
class UserData(...) {
operator fun plus(anotherData: UserData): UserData {
val firstSale = min(this.firstSale, anotherData.firstSale)
val taxList = concatenateItemSales(this.taxList, anotherData.taxList)
val salesList = conatenateItemSales(this.salesList, anotherData.salesList)
val taxTotal = this.taxTotal anotherData.taxTotal
// and similarly calculate the other fields you need
return UserData(firstSale, ..., taxList, salesList, taxTotal, ...)
}
}
Now you can sum / concatenate two UserData instances just using
operator like this:
val userData1: UserData = UserData(...)
val userData2: UserData = UserData(...)
val conacatenatedUserData = userData1 userData2
CodePudding user response:
As you asked specifically for taxList
and salesList
, I'll ignore all other properties and focus on them. We can calculate the sum of taxList
with this code:
val data: List<UserData> = ...
val taxList = data.asSequence()
.flatMap { it.taxList }
.groupingBy { it.itemDescription }
.fold(0L) { acc, item -> acc item.itemsTotalValue }
.map { ItemSale(it.key, it.value) }
We will go trough this code step by step:
flatMap()
merges alltaxList
properties from allUserData
objects into a single list ofItemSale
objects. It contains duplicates, e.g. multiplefirst
items.groupingBy()
groupsItemSale
items by their name/description. We can then easily work on e.g.first
items separately from other items.fold()
is where summing really happens. It works on each above group separately, it starts summing with0L
value and with each new item (e.g. each newfirst
item), it addsitemsTotalValue
to the accumulated value.- At this point we have a
Map<String, Long>
where keys areitemDescription
strings and values are summeditemsTotalValue
properties of items with this specificitemDescription
. - Finally, we need to convert the map into a list of
ItemSale
which we do inmap
step.
As we use asSequence()
and groupingBy()
(instead of groupBy()
), all data is calculated lazily. That means it works as we would iterate over all items sequentially, calculating the result while iterating. This is good for performance reasons.
salesList
can be calculated in exactly the same way, we just need to modify flatMap()
step.