j_muco998
j_muco998

Reputation: 3

Combine two LiveData<Int> into one LiveData<List<Pair<Int, Int>>>

I want to display a dot plot chart with points having calories consumed as x coordinates and calories burned as y cordinates. I retreive both of the calories in my ViewModel but i want to combine them in a single LiveData<List<Pair<Int, Int>>>. This way i can observe this LiveData in my fragment and access the list of Pairs having the x and y coordinates (calories).

This is my DAO where i have the two liveDatas of type List<Int>:

@Query("SELECT amount from calories_table WHERE strftime('%m-%d-%Y', timestamp, 'unixepoch') IN (SELECT strftime('%m-%d-%Y', timestamp, 'unixepoch') FROM running_table)")
fun getCaloriesConsumedOnRunDays(): LiveData<List<Int>>

@Query("SELECT caloriesBurned FROM running_table")
fun getCaloriesFromRuns():LiveData<List<Int>>

This is my repository:

fun getCaloriesConsumedOnRunDays() = runDao.getCaloriesConsumedOnRunDays()

fun getCaloriesFromRuns() = runDao.getCaloriesFromRuns()

Lastly i create these two variables in my ViewModel that store the two liveDatas:

private val getCaloriesConsumedOnRunDays = mainRepository.getCaloriesConsumedOnRunDays()

private val getCaloriesFromRuns = mainRepository.getCaloriesFromRuns()

Upvotes: 0

Views: 403

Answers (1)

Billy
Billy

Reputation: 1374

First of all, this being your first question, welcome.

There are two approaches to what you're trying to do.

  1. This is specific to what you're trying to do... I would recommend just pulling all the data out in a single SQLite query call, would be the more efficient way to go. SQLite runs at C-level speed, you'd rather let it do all the heavy lifting instead of doing glue code with Kotlin on the VM. (Also, would avoid using a sub-select, plus if you're just doing WHERE .. IN; then there's no reason to do strftime around them, since you're just trying to do straight matching).

This could be a departure of what you were thinking, but something like this:

@Query("""
SELECT calories_table.amount, running_table.caloriesBurned 
FROM calories_table 
INNER JOIN running_table ON calories_table.timestamp = running_table.timestamp")
"""
fun getCaloriesConsumedOnRunDays(): LiveData<List<Pair<Int, Int>>>

(if ROOM precompiler doesn't like having Pair<Int, Int> there, make a new data class, something like data class CaloriesInfo(val amount: Int, val caloriesBurned: Int) and use that as your return data type instead)

This way you just a need a single LiveData.

  1. Sometimes you can have legitimate usecase that you need to merge two or more LiveData together, in which case you should use MediatorLiveData.

Something like:

val mediator: MediatorLiveData<Pair<Int, Int>> = MediatorLiveData()

val liveData1 = repo.getLiveData()
val liveData2 = repo.getAnotherLiveData()

mediator.addSource(liveData1) { mediator.postValue(it to liveData2.value) }
mediator.addSource(liveData2) { mediator.postValue(liveData1.value to it) }

There are sometimes good reasons to do this, but I don't recommend this for what you're saying you're trying to do, as you need to do additional things to get the data in the right format that would be useful for you to plot. LiveData runs asynchronously, so it doesn't make sense for x and y to come from different LiveData when you're trying to plot things. Your best bet is still do a nicer SQLite query and do it in a single query.

Upvotes: 1

Related Questions