Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

462
Views
Kotlin - How to properly map result using Either?

I'm trying to read a list of objects from the database and mapping it to another type of list.

// Returns either a Failure or the expected result
suspend fun getCountries(): Either<Failure, List<CountryItem>> {
    // Get the result from the database
    val result = countryLocalDataSource.getCountries()

    // Left means Failure
    if (result.isLeft) {
        // Retrieve the error from the database
        lateinit var error: Failure
        result.either({
            error = it
        }, {})

        // Return the result
        return Either.Left(error)
    }

    // The database returns a List of Country objects, we need to map it to another object (CountryItem)
    val countryItems: MutableList<CountryItem> = mutableListOf()

    // Iterate the Country List and construct a new List of CountryItems
    result.map { countries -> {
        countries.forEach {
            // Assign some values from an Enum (localized string resources)
            val countryEnumValue = Countries.fromId(it.id)
            countryEnumValue?.let { countryIt ->
                val countryStringNameRes = countryIt.nameStringRes;

                // Create the new CountryItem object (@StringRes value: Int, isSelected: Bool)
                countryItems.add(CountryItem(countryStringNameRes, false))
            }
        }
    } }

    // Because this is a success, return as Right with the newly created List of CountryItems
    return Either.Right(countryItems)
}

For the sake of readability I didn't included the whole Repository or the DAO classes and I have left comments in the code snippet above.

In a nutshell: I'm using Kotlin's Coroutines for accessing the database in a separated thread and I'm handling the response on the UI Thread. Using the Either class in order to return two different results (failure or success).

The above code works, however It's too ugly. Is this the right approach to deliver the result?

What I'm trying to do is to refactor the code above.

The whole problem is caused by the two different object types. The Database Data Source API is returning an Either<Failure, List<Country>>, meanwhile the function is expected to return an Either<Failure, List<CountryItem>>.

I can't deliver a List<CountryItem> directly from the Database Data Source API, because Android Studio doesn't let me compile the project (entities implementing interfaces, compile error, etc.). What I'm trying to achieve is to map the Either result in a nicer way.

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

Try using Kotlin's Result

So in your case you can write something like:

return result.mapCatching { list: List<Country> -> /*return here List<CountryItem>>*/ }

And for checking result call:

result.fold(
    onSuccess = {...},
    onFailure = {...}
)

In order to invoke a constructor you should call Result.success(T) or Result.failure(Throwable)

Unfortunately, you'll also need to suppress use-as-return-type warnings How to

over 4 years ago · Santiago Trujillo Report

0

You can simplify by checking the type of Either and accessing the value directly. In your case:

access Left via result.a -> Failure

access Right via result.b -> List<Country>

ex:

when (result) {
    is Either.Left -> {
        val failure: Failure = result.b
        ...
    }
    is Either.Right -> {
        val countries: List<Country> = result.b
        ...
    }
}

An alternative is to use the either() function (normally this is called fold()):

result.either(
    { /** called when Left; it == Failure */ }, 
    { /** called when Right; it == List<Country> */ }
)
over 4 years ago · Santiago Trujillo Report

0

Assume your Country class is defined as follow:

data class Country(val name: String) {}

and your CountryItem class is defined as follow:

data class CountryItem(private val name: String, private val population: Int) {}

and your CountryLocalDataSource class with a method getCountries() like this:

class DataSource {
    suspend fun getCountries(): Either<Exception, List<Country>> {
        return Either.Right(listOf(Country("USA"), Country("France")))
        //return Either.Left(Exception("Error!!!"))
    }
}

then the answer to your question would be:

suspend fun getCountryItems(): Either<Exception, List<CountryItem>> {        
    when (val countriesOrFail = DataSource().getCountries()) {
        is Either.Left -> {
            return Either.Left(countriesOrFail.a)
        }
        is Either.Right -> {
            val countryItems = countriesOrFail.b.map {
                CountryItem(it.name, 1000)
            }
            return Either.Right(countryItems)
        }        
    }    
}

To call your getCountryItems(), here is an example:

suspend fun main() {
    when (val countriesOrFail = getCountryItems()) {
        is Either.Left -> {
            println(countriesOrFail.a.message)
        }
        is Either.Right -> {
            println(countriesOrFail.b)       
        }
    }
}

Here's the sample code in the playground: https://pl.kotl.in/iiSrkv3QJ

A note about your map function:

I'm guessing you don't actually need the result to be a MutableList<CountryItem> but you had to define so because you want to add an element as you iterate through the input list List<Country>.

Perhaps the following is the case: If you have a List<Country> with 2 elements like in the example, and you want to map so that the result becomes a List<CountryItem> with also 2 corresponding elements, then you don't need to call forEach inside a fun that gets passed to the higher-order function map. But this may be an entirely new question.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!