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

Viewed 79

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:

  • A Set of Identifiers that exist and are valid in a given context. Example:
val existentIdentifiers = setOf("A-1", "A-2", "B-1", "B-2", "C-1")
  • A Map that contains a List of Data objects per Identifier. Example:
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"))
)
  • A List of Lists that group together the Identifiers that should share the same List<Data> (each List includes always 2 Identifiers). Example
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?

1 Answers

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.

Related