Home > Back-end >  Sub-collection sum
Sub-collection sum

Time:09-21

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:

  1. flatMap() merges all taxList properties from all UserData objects into a single list of ItemSale objects. It contains duplicates, e.g. multiple first items.
  2. groupingBy() groups ItemSale items by their name/description. We can then easily work on e.g. first items separately from other items.
  3. fold() is where summing really happens. It works on each above group separately, it starts summing with 0L value and with each new item (e.g. each new first item), it adds itemsTotalValue to the accumulated value.
  4. At this point we have a Map<String, Long> where keys are itemDescription strings and values are summed itemsTotalValue properties of items with this specific itemDescription.
  5. Finally, we need to convert the map into a list of ItemSale which we do in map 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.

  • Related