How to explode a list into a Map in kotlin? - kotlin

Imagine you want to transform this:
val initialValues: List<Pair<String, String>>
where the first String represents a key, the second a value
into a map:
val finalMap: Map<String,String>
containing each pair item twice, the first with the original key, the second one with a sort of expanded key.
How would you do that? Currently I'm using a
val finalMap = mutableMapOf<String, String>()
that I use while I'm iterating over initialValues. But I really don't like it.
initialValues.forEach {
val explodedPairs:List<Pair<String,String>> = <do-something>
explodedPairs.forEach { finalMap.put(it.first, it.second) }
}
how would you do more assertively?

You can use associate / associateBy like this -
val map1 = initialList.associate { it.first to it.second }
println(map1.toString()) //{1=x, 2=y}
val map2 = initialList.associateBy({it.first},{it.second})
println(map2.toString()) //{1=x, 2=y}
You can also use toMap and do this -
val map3 = initialList.toMap()
println(map3.toString()) //{1=x, 2=y}
where this is my initialList declaration -
val initialList = listOf(Pair(1, "x"), Pair(2, "y"))

You can use associate and associateby

You could use mapOf like this:
val initialValues: List<Pair<String, String>> = listOf()
val final = mapOf(*initialValues.toTypedArray())
But #Supriya has the better answer using toMap

Related

Kotlin how convert map with long and Pair to other map?

I have a construct like this:
var values = mutableMapOf<Long, Pair<String, Boolean>>()
No I need to have one map from this above like this:
val transformedMap = Map<Long, String>
I cannot transform this first to second. Andy suggestions?
You can do
val transformedMap = values.mapValues { it.value.first }
Most of iterators will require a variable for storing the resulting map, but I think associate can do it directly:
values.entries.associate {
it.key to it.value.first
}
The other options are like this
val output = mutableMapOf<Long, String>()
//values iterator, for, forEach, keys, etc
output[key] = values[key]?.first

Kotlin Creating List<List<Map<String, String>>>

I am trying to return List<List<Map<String, String>>> from a function in kotlin. I'm new to kotlin.
Edit1
Here's how I am attempting to to this
val a = mutableListOf(mutableListOf(mutableMapOf<String, String>()))
The problem with the above variable is, I am unable to figure out how to insert data into this variable. I tried with this:
val a = mutableListOf(mutableListOf(mutableMapOf<String, String>()))
val b = mutableListOf(mutableMapOf<String, String>())
val c = mutableMapOf<String, String>()
c.put("c", "n")
b.add(c)
a.add(b)
This is giving me:
[[{}], [{}, {c=n}]]
What I want is [[{c=n}]]
Can someone tell me how I can insert data into it?
The end goal I am trying to achieve is to store data in the form of List<List<Map<String, String>>>
EDIT 2
The function for which I am trying to write this dat structure:
fun processReport(file: Scanner): MutableList<List<Map<String, String>>> {
val result = mutableListOf<List<Map<String, String>>>()
val columnNames = file.nextLine().split(",")
while (file.hasNext()) {
val record = mutableListOf<Map<String, String>>()
val rowValues = file.nextLine()
.replace(",(?=[^\"]*\"[^\"]*(?:\"[^\"]*\"[^\"]*)*$)".toRegex(), "")
.split(",")
for (i in rowValues.indices) {
record.add(mapOf(columnNames[i] to rowValues[i]))
print(columnNames[i] + " : " + rowValues[i] + " ")
}
result.add(record)
}
return result
}
You don't need to use mutable data structures. You can define it like this:
fun main() {
val a = listOf(listOf(mapOf("c" to "n")))
println(a)
}
Output:
[[{c=n}]]
If you wanted to use mutable data structures and add the data later, you could do it like this:
fun main() {
val map = mutableMapOf<String, String>()
val innerList = mutableListOf<Map<String, String>>()
val outerList = mutableListOf<List<Map<String, String>>>()
map["c"] = "n"
innerList.add(map)
outerList.add(innerList)
println(outerList)
}
The output is the same, although the lists and maps are mutable.
In response to the 2nd edit. Ah, you're parsing a CSV. You shouldn't try to do that yourself, but you should use a library. Here's an example using Apache Commons CSV
fun processReport(file: File): List<List<Map<String, String>>> {
val parser = CSVParser.parse(file, Charset.defaultCharset(), CSVFormat.DEFAULT.withHeader())
return parser.records.map {
it.toMap().entries.map { (k, v) -> mapOf(k to v) }
}
}
For the following CSV:
foo,bar,baz
a,b,c
1,2,3
It produces:
[[{foo=a}, {bar=b}, {baz=c}], [{foo=1}, {bar=2}, {baz=3}]]
Note that you can simplify it further if you're happy returning a list of maps:
fun processReport(file: File): List<Map<String, String>> {
val parser = CSVParser.parse(file, Charset.defaultCharset(), CSVFormat.DEFAULT.withHeader())
return parser.records.map { it.toMap() }
}
Output:
[{foo=a, bar=b, baz=c}, {foo=1, bar=2, baz=3}]
I'm using Charset.defaultCharset() here, but you should change it to whatever character set the CSV is in.

Can I add a field of MVoice in List to mutableSetOf() with Kotlin?

I can use Code A add all id of MVoice in a List to val selectedIDs: MutableSet<Int>.
I think I can simplify it, and add all id of List at one time, but Code B doesn't work, how can I fix it?
Code A
val selectedIDs: MutableSet<Int> = mutableSetOf()
val listVoiceBySort: LiveData<List<MVoice>> =_listVoiceBySort
listVoiceBySort.value?.forEach(){
selectedIDs.add(it.id)
}
Code B
val selectedIDs: MutableSet<Int> = mutableSetOf()
val listVoiceBySort: LiveData<List<MVoice>> =_listVoiceBySort
listVoiceBySort.value?.let{
selectedIDs.addAll(it.id)
}
This should work:
val selectedIDs: MutableSet<Int> = mutableSetOf()
val listVoiceBySort: LiveData<List<MVoice>> =_listVoiceBySort
listVoiceBySort.value?.map { it.id }?.let {
selectedIDs.addAll(it)
}

How to map list of Triple into data class with nested list in kotlin

Sample
val listTriple = listOf<Triple<Int, Int, String>>()
data class Sample(val parentId: Int, val listItem : List<Item>)
data class Item(val id: Int, val name: String)
how to map listTriple into listOf Sample in kotlin in the best way
You can express that really concise by specifying groupBys valueTransform lambda:
val samples = listTriple.groupBy({ it.first }, { Item(it.second, it.third) }).map {
Sample(it.key, it.value)
}
But as EpicPandaForce mentioned in the comments it would be better to create a dedidacted class instead of using Triple. Having to refer to properties by first, second, third makes it hard to read.
Of course I could've just destructuring syntax here as well, but that doesn't solve the problem of not having a dedicated class.
Try this:
val samples = list
.groupBy { it.first }
.map {
val items = it.value.map { Item(it.second, it.third) }
Sample(it.key, items)
}
Assuming that the first Int of your triple is the parentId and the second one is the id, you would do this:
val listOfSample = listTriple.map {(parentId, id, name) ->
Sample(parentId, listOf(Item(id, name)))
}
If they are in the other order, just change the order in the '.map{}'
You can do groupBy for the secondary question of how to concatenate all lists based on the same parent id
val listOfSample = listTriple.map {(parentId, id, name) ->
Sample(parentId, listOf(Item(id, name)))
}.groupBy { it.parentId }

Convert String referential datatype to real referential datatypes

I have the following dataclasses:
data class JsonNpc(
val name: String,
val neighbours: JsonPreferences
)
data class JsonPreferences(
val loves: List<String>,
val hates: List<String>
)
I have a list of these, and they reference each other through strings like:
[
JsonNpc(
"first",
JsonPreferences(
listOf("second"),
listOf()
)
),
JsonNpc(
"second",
JsonPreferences(
listOf(),
listOf("first")
)
)
]
note that a likes b does not mean b likes a
I also have the Dataclasses
data class Npc(
val name: String,
val neighbours: NeighbourPreferences,
)
data class NeighbourPreferences(
val loves: List<Npc>,
val hates: List<Npc>
)
And I want to convert the String reference types to the normal reference types.
What I have tried:
recursively creating the npcs (and excluding any that are already in the chain, as that would lead to infinite recursion):
Does not work, as the Npc can not be fully created and the List is immutable (I dont want it to be mutable)
I have managed to find a way to do this. It did not work with Npc as a data class, as I needed a real constructor
fun parseNpcs(map: Map<String, JsonNpc>): Map<String, Npc> {
val resultMap: MutableMap<String, Npc> = mutableMapOf()
for (value in map.values) {
if(resultMap.containsKey(value.name))
continue
Npc(value, map, resultMap)
}
return resultMap
}
class Npc(jsonNpc: JsonNpc, infoList: Map<String, JsonNpc>, resultMap: MutableMap<String, Npc>) {
val name: String
val neighbourPreferences: NeighbourPreferences
init {
this.name = jsonNpc.name
resultMap[name] = this
val lovesNpc = jsonNpc.neighbours.loves.map {
resultMap[it] ?: Npc(infoList[it] ?: error("Missing an Npc"), infoList, resultMap)
}
val hatesNpc = jsonNpc.neighbours.hates.map {
resultMap[it] ?: Npc(infoList[it] ?: error("Missing an Npc"), infoList, resultMap)
}
this.neighbourPreferences = NeighbourPreferences(
lovesNpc, hatesNpc
)
}
}
data class NeighbourPreferences(
val loves: List<Npc>,
val hates: List<Npc>
)
checking in the debugger, the people carry the same references for each Neighbour, so the Guide is always one Npc instance.