Transformations. Difference between Map and SwitchMap

Many times I found myself searching the difference between Map and SwitchMap, and for some reason, I couldn’t get the hang of it and when I should use each of them.

So, I’ll play around with them a little to see how to use them what is the difference between them. For me, at first glance, it didn’t seem like a big difference.

Map

On the documentation it says:

Returns a LiveData mapped from the input source LiveData by applying mapFunction to each value set on source.

My first thought is that what will happen is to apply a function on each value of LiveData and change the LiveData by applying this function. But once I thought about it a little more, I realized that this doesn’t make much sense. So, what this does is to actually transform each value of LiveData into a new data (or type if you want) and then passing it on forward.

SwitchMap

About SwitchMap in the documentation it says:

Returns a LiveData mapped from the input source LiveData by applying switchMapFunction to each value set on source.

The returned LiveData delegates to the most recent LiveData created by calling switchMapFunction with the most recent value set to source, without changing the reference. In this way, switchMapFunction can change the 'backing' LiveData transparently to any observer registered to the LiveData returned by switchMap().

From the first sentence, it doesn’t seem like much of a difference from Map. In fact, switchMapFunction receives a source and returns a new LiveData. Meanwhile, mapFunction returns a data type, that is forwarded by the Transformation.map() into a LiveData.

The Experiment

For this experiment I set up a repository that provides a list of contacts.

object ContactsRepository { private val contacts: MutableList<Contact> = mutableListOf( Contact(1, "John", "Doe"), Contact(2, "Billy", "Bob"), Contact(3, "Anton", "Potter") ) fun getFilteredContacts(name: String): LiveData<List<Contact>> { return MutableLiveData(contacts.filter { it.firstName.contains(name) || it.lastName.contains(name) }) } }

In fragment I have an EditText where the user can write a name and as he writes the list of contacts gets updated. The EditText name is hooked to a LiveData<String> with two way databinding and here we just setup the list adapter and a default value for the name in order to show the complete list of contacts at the beginning when nothing is written in the name field.

If you need more details about updating the list adapter you can find them in this article.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) subscribeObservers() list_contacts.adapter = mAdapter list_contacts.addItemDecoration(DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)) viewModel.name.value = "" } private fun subscribeObservers() { viewModel.getContactsMapFiltered().observe(viewLifecycleOwner, Observer { if (it != null) { mAdapter.updateItems(it) } }) }

Now the ViewModel where the magic happens.

class ContactsViewModel : ViewModel() { val name: MutableLiveData<String> = MutableLiveData() private val contacts = getContactsSwitchMap() fun getContactsMapFiltered(): LiveData<List<ContactUi>> { return Transformations.map(contacts) { contactList -> val newContacts: MutableList<ContactUi> = mutableListOf() contactList.forEach { newContacts.add(ContactUi(it.id, "${it.firstName} ${it.lastName}")) } newContacts } } private fun getContactsSwitchMap(): LiveData<List<Contact>> { return Transformations.switchMap(name) { nameQuery -> ContactsRepository.getFilteredContacts(nameQuery) } } }

There are two things that we need to do here:

  1. We must filter the contact list by using the name LiveData.
  2. We must return a list of ContactUi which is an object needed by the contact list.

With these use cases first, we need to filter the contact list by name. For this SwitchMap seems like a good choice because we already have a method in the repository that returns a LiveData with the filtered contacts by name — getFilteredContacts(). So, we can switch the name LiveData for the filtered contacts LiveData. In this example, the filtering is done locally, but it can also be a LiveData returned from the database or a network data source.

Now for the second part, we must take the List<Contact> and change it to List<ContactUi>. For this, we can use the map function because we just change a data type to another data type (from List<Contact> to List<ContactUi>). What this does is to take the list of contacts LiveData as a source and change it to a different type of list, prepared for the UI. The result of this method is observed in the fragment and the list gets updated.

The result looks something like this.

Conclusion

When you just have to change a data type to another data type (let’s say by using the first data type contents; Contact to ContactsUi or User to String by using first and last name as described here) you should use .

When you have to change an input LiveData to a different LiveData (from a name, fetch a list of users) the choice should be a switchMap() because you actually change a LiveData with another.

I hope this example clears some things up and shows when it’s appropriate to use map() or switchMap(). If you have any thoughts on this, leave a comment below and check other experiments.

We are Android Developers.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store