Search code examples
androidkotlinsortingstream

Filtering list take so long in kotlin


I am working on an existing project. Below function taking long .

All other api's response display less than in two seconds. But PersonalOffer took long to display . I think the problem is in below sorting. Mapping is also done multiple times.

fun getPersonalOffers(): Flow<List<PersonalOffer>> {
        return personalOfferDao.getPersonalOffers().map { list ->
            list.mapNotNull { PersonalOffer.mapFromDB(it) }.sortedBy { it.expiryDate }.sortedByDescending { it.isPlus }
        }
    }

How can I improve it to reduce it's compilation time.

PersonalOfferDao.kt

@Dao
interface PersonalOfferDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertAll(offers: List<DBPersonalOffer>?)

    @Query("SELECT * FROM personal_offer_table")
    fun getPersonalOffers(): Flow<List<DBPersonalOffer>>

    @Query("DELETE FROM personal_offer_table")
    suspend fun deleteAll()
}

DBPersonalOffer.kt

@Entity(tableName = DBPersonalOffer.TABLE_NAME)
data class DBPersonalOffer(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "disclaimerText")
    val disclaimerText: String?,
    @ColumnInfo(name = "extraInfoText")
    val extraInfoText: String?,
    @ColumnInfo(name = "discountName")
    val discountName: String?,
    @ColumnInfo(name = "discountPercent")
    val discountPercent: String?,
    @ColumnInfo(name = "expiryDate")
    val expiryDate: String?,
    @ColumnInfo(name = "futureTeaserText")
    val futureTeaserText: String?,
    @ColumnInfo(name = "prizeId")
    val prizeId: String?,
    @ColumnInfo(name = "productName")
    val productName: String?,
    @ColumnInfo(name = "teaserText")
    val teaserText: String?,
    @ColumnInfo(name = "flowType")
    val flowType: String?,
    @ColumnInfo(name = "completeText")
    val completeText: String?,

    @Embedded(prefix = "images_")
    val images: DBPersonalOfferImages?,
) {
    companion object {

        const val TABLE_NAME = "personal_offer_table"

        fun mapHttpResponse(response: NetworkPersonalOffer): DBPersonalOffer {
            return DBPersonalOffer(
                disclaimerText = response.disclaimerText,
                discountName = response.discountName,
                discountPercent = response.discountPercent,
                expiryDate = response.expiryDate,
                futureTeaserText = response.futureTeaserText,
                prizeId = response.prizeId,
                productName = response.productName,
                teaserText = response.teaserText,
                flowType = response.flowType,
                completeText = response.completeText,
                images = DBPersonalOfferImages.mapHttpResponse(response.imageUrls),
                extraInfoText = response.extraInfoText
            )
        }
    }
}

PersonalOffer.kt

@Parcelize
data class PersonalOffer(
    val id: Int,
    val disclaimerText: String?,
    val discountName: String?,
    val discountPercent: String?,
    val expiryDate: Date?,
    val futureTeaserText: String?,
    val prizeId: String?,
    val productName: String?,
    val teaserText: String?,
    val images: PersonalOfferImages?,
    var isPlus: Boolean,
    var flowType: String?,
    var completeText:String?,
    var extraInfoText:String?
) : Parcelable ,TrackableObject(){

    companion object {
        fun mapFromDB(dbPersonalOffer: DBPersonalOffer?): PersonalOffer? {
            dbPersonalOffer?.let {

                val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.UK)
                var expiryDate: Date? = try {
                    sdf.parse(it.expiryDate)
                } catch (ex: Exception) {
                    null
                }

                return PersonalOffer(
                    id = it.id,
                    disclaimerText = it.disclaimerText,
                    discountName = it.discountName,
                    discountPercent = it.discountPercent,
                    expiryDate = expiryDate,
                    futureTeaserText = it.futureTeaserText,
                    prizeId = it.prizeId,
                    productName = it.productName,
                    teaserText = it.teaserText,
                    isPlus = it.flowType == "Club Matas Plus",
                    completeText = it.completeText?:"",
                    flowType = it.flowType,
                    images = PersonalOfferImages.mapFromDB(it.images),
                    extraInfoText = it.extraInfoText
                )
            } ?: run {
                return null
            }
        }
    }

    override fun getClickEventName() = "personal_offer_in_store_mode_clicked"

    override fun getViewEventName() = "personal_offer_in_store_mode_viewed"

    override fun getTrackParams(): MutableSet<Pair<String, Any>> = mutableSetOf(
        Pair("name", discountName ?: ""),
        Pair("prize_id", prizeId ?: "")
    )
}

Solution

  • you can move the filter logic to the db by writing a query that will perform the needed filter, You can also save the boolean value of isPlus on the entity so that you just filter once from the data layer

    @Query("SELECT * FROM personal_offer_table ORDER BY date(expiryDate) ASC, isPlus DESC")
    fun getPersonalOffersSorted(): Flow<List<DBPersonalOffer>>