Skip to content

Map and groupBy values in a stream

I have a wrapper that may or may not contain data:

class EmailAdapter {
...
    fun toEmail(): Optional<Email> {
        val agencyContact = getAgencyContact()
        return if (agencyContact.isPresent) {
            Optional.of(Email(
                contact = agencyContact.get()
            ))
        } else {
            Optional.empty()
        }
    }
}

I group one user to multiple contacts:

fun upsertAll(eMails: List<EmailAdapter>) {

    val mailsByUser = eMails
        .filter { adapter -> adapter.getUserContact().isPresent }
        .filter { adapter -> adapter.getAgencyContact().isPresent }
        .groupBy { adapter -> adapter.getUserContact().get() }

It groups val mailsByUser: Map<String, List<EmailAdapter>>
I want to group all emails to a unique user
I want to unwrap the EmailAdapter so that the relation is EmailAdapter.user -> List<EmailAdapter.mail> or val mailsByUser: Map<String, List<Email>>

I fail in the last step – on a conceptual level.

Answer

Does this work for you (first attempt at Kotlin) ?

var mailsByUser = eMails
    .filter { adapter -> adapter.getAgencyContact().isPresent }
    .filter { adapter -> adapter.getUserContact().isPresent }
    .groupBy { adapter -> adapter.getUserContact().get() }
    .mapValues { adapter -> adapter.value.map { ea -> ea.toEmail().get() } }

Conceptually this requires another filter/exception handling for empty optional via toEmail. Similarly one of your methods could be simplified as

fun toEmail(): Optional<Email> {
    return getAgencyContact()
        .map { ac -> Email(contact = ac) } // use of 'map' here
}