João Dias
João Dias

Reputation: 17470

How to add data to Map copying existing values based on List of identifiers

Sorry for the poor title but it is rather hard to describe my use case in a short sentence.

Context

I have the following model:

typealias Identifier = String

data class Data(val identifier: Identifier,
                val data1: String,
                val data2: String)

And I have three main data structures in my use case:

val existentIdentifiers = setOf("A-1", "A-2", "B-1", "B-2", "C-1")
val dataPerIdentifier: Map<Identifier, List<Data>> = mapOf(
    "A-1" to listOf(Data("A-1", "Data-1-A", "Data-2-A"), Data("A-1", "Data-1-A", "Data-2-A")),
    "B-1" to listOf(Data("B-1", "Data-1-B", "Data-2-B")),
    "C-1" to listOf(Data("C-1", "Data-1-C", "Data-2-C"))
)
val identifiersWithSameData = listOf(listOf("A-1", "A-2"), listOf("B-1", "B-2"))

Problem / Use Case

The problem that I am trying to tackle stems from the fact that dataPerIdentifier might not contain all identifiersWithSameData given that existentIdentifiers contains such missing Identifiers. I need to add those missing Identifier to dataPerIdentifier, copying the List<Data> already in there.

Example

Given the data in the Context section:

A-1=[Data(identifier=A-1, data1=Data-1-A, data2=Data-2-A), 
     Data(identifier=A-1, data1=Data-1-A, data2=Data-2-A)], 
B-1=[Data(identifier=B-1, data1=Data-1-B, data2=Data-2-B)], 
C-1=[Data(identifier=C-1, data1=Data-1-C, data2=Data-2-C)]

The desired outcome is to update dataPerIdentifier so that it includes:

A-1=[Data(identifier=A-1, data1=Data-1-A, data2=Data-2-A), 
     Data(identifier=A-1, data1=Data-1-A, data2=Data-2-A)], 
B-1=[Data(identifier=B-1, data1=Data-1-B, data2=Data-2-B)], 
C-1=[Data(identifier=C-1, data1=Data-1-C, data2=Data-2-C)], 
A-2=[Data(identifier=A-2, data1=Data-1-A, data2=Data-2-A), 
     Data(identifier=A-2, data1=Data-1-A, data2=Data-2-A)]

The reason is that existentIdentifiers contains A-2 that is missing in the initial dataPerIdentifier Map. B-2 is also missing in the initial dataPerIdentifier Map but existentIdentifiers does not contain it, so it is ignored.

Possible solution

I have already a working code (handleDataForMultipleIdentifiers() method is the one doing the heavy lifting), but it does not feel to be the cleanest or easiest to read:

fun main(args: Array<String>) {
    val existentIdentifiers = setOf("A-1", "A-2", "B-1", "C-1")

    val dataPerIdentifier: Map<Identifier, List<Data>> = mapOf(
        "A-1" to listOf(Data("A-1", "Data-1-A", "Data-2-A"), Data("A-1", "Data-1-A", "Data-2-A")),
        "B-1" to listOf(Data("B-1", "Data-1-B", "Data-2-B")),
        "C-1" to listOf(Data("C-1", "Data-1-C", "Data-2-C"))
    )

    val identifiersWithSameData = listOf(listOf("A-1", "A-2"), listOf("B-1", "B-2"))

    print("Original Data")
    println(dataPerIdentifier)

    print("Target Data")
    println(dataPerIdentifier.handleDataForMultipleIdentifiers(identifiersWithSameData, existentIdentifiers))
}

fun Map<Identifier, List<Data>>.handleDataForMultipleIdentifiers(identifiersWithSameData: List<List<Identifier>>, existentIdentifiers: Set<Identifier>)
        : Map<Identifier, List<Data>> {
    val additionalDataPerIdentifier = identifiersWithSameData
        .mapNotNull { identifiersList ->
            val identifiersWithData = identifiersList.find { it in this.keys }
            identifiersWithData?.let { it to identifiersList.minus(it).filter { it in existentIdentifiers } }
        }.flatMap { (existentIdentifier, additionalIdentifiers) ->
            val existentIdentifierData = this[existentIdentifier].orEmpty()
            additionalIdentifiers.associateWith { identifier -> existentIdentifierData.map { it.copy(identifier = identifier) } }.entries
        }.associate { it.key to it.value }

    return this + additionalDataPerIdentifier
}

typealias Identifier = String

data class Data(val identifier: Identifier,
                val data1: String,
                val data2: String)

So my question is: how can I do this in a simpler way?

Upvotes: 2

Views: 88

Answers (1)

broot
broot

Reputation: 28362

If identifiersWithSameData always contains 2 identifiers per item then it should not really be a list of lists, but rather a list of pairs or dedicated data classes. And if you convert this data structure into a map like this:

val identifiersWithSameData = mapOf("A-1" to "A-2", "A-2" to "A-1", "B-1" to "B-2", "B-2" to "B-1")

The the whole solution is pretty simple:

existentIdentifiers.associateWith {
    dataPerIdentifier[it] ?: dataPerIdentifier[identifiersWithSameData[it]!!]!!
}

I'm not sure about both !!, for example I don't know if it is guaranteed that identifier existing in existentIdentifiers exists in identifiersWithSameData as well. You may need to tune this solution a little.

Upvotes: 1

Related Questions