How to filter LiveData List based on LiveData Map?

606 views Asked by At

I have to asynchronous operations. In the viewmodel, the two need to work together for the UI. How can I filter the LiveData list based on the keys in de LiveData map? (Object id's in list correspond to Map keys)

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
private val _allJourneys = MutableLiveData<List<Journey>>()
val allJourneys: LiveData<List<Journey>> get() = _allJourneys

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
private val _enrolledMap = MutableLiveData<Map<String, String>>()
val enrolledMap: LiveData<Map<String, String>> get() = _enrolledMap

fun getEnrolled() {
    viewModelScope.launch {
        progressRepository.getEnrolledJourneysMapOfUser().observeForever {
            Timber.d("Map values: $it")
            _enrolledMap.value = it
        }
    }

}

fun getJourneys() {
    viewModelScope.launch {
        journeysRepository.getAll().observeForever { it ->
            _allJourneys.value = it.filter {
               // enrolledMap.containsKey(it.id) ??? Nullpointer
            }
        }
    }
}
2

There are 2 answers

3
Stachu On

how about something like this (based on the MediatorLiveData example from here)

    val allJourneys: LiveData<List<Journey>> = journeysRepository.getAll()
    val enrolledMap: LiveData<Map<String, String>> = progressRepository.getEnrolledJourneysMapOfUser()

    private val _filteredJourneys = MediatorLiveData<List<Journey>>()
    private val filteredJourneys: LiveData<List<Journey>> = _filteredJourneys

    init {
        _filteredJourneys.addSource(allJourneys) {
            combineLatestData(allJourneys,enrolledMap)
        }
        _filteredJourneys.addSource(enrolledMap) {
            combineLatestData(allJourneys,enrolledMap)
        }
    }

    private fun combineLatestData(
        journeysLD: LiveData<List<Journey>>,
        enrolledLD: LiveData<Map<String, String>>
    ): List<Journey>? {
        val j = journeysLD.value
        val e = enrolledLD.value
        val result = listOf<Journey>()
        if (j == null || e == null) {
            return result // TODO filter j by e
        }
        return null
    }

the changes in the allJourney and enrolledMap LiveData should trigger the combineLatestData

1
EpicPandaForce On

observeForever is wrong approach inside a ViewModel. Each time get__ is called, a new subscription would be made, and you will get multiple subscriptions and possibly memory leaks inside your ViewModel until the garbage collector clears the stuck subscriptions.

Instead, as you are using LiveData, you should just use the return values and keep them as fields.

val allJourneys: LiveData<List<Journey>> get() = journeysRepository.getAll()
    
val enrolledMap: LiveData<Map<String, String>> get() = progressRepository.getEnrolledJourneysMapOfUser()

//fun getEnrolled() {
//    viewModelScope.launch {
//        .observeForever {
//            Timber.d("Map values: $it")
//            _enrolledMap.value = it
//        }
//    }
//
//}

//fun getJourneys() {
//    viewModelScope.launch {
//        .observeForever { it ->
//            _allJourneys.value = it.filter {
//               // enrolledMap.containsKey(it.id) ??? Nullpointer
//            }
//        }
//    }
//}

As LiveData from a Repository that comes from a DAO will already handle background data fetch.

If this is done manually with suspending functions in the Repository, then you can use switchMap + liveData { emitSource - but in this case, it does not seem necessary.