kotlin merge list of map - kotlin

I want to group by id.
[
{
"id": 1,
"name": "a"
},
{
"id": 2,
"name": "b"
},
{
"id": 3,
"name": "c"
}
]
The results should be as follows:
{
"1": "a",
"2": "b",
"3": "c"
}
What is the most idiomatic way of doing this in Kotlin?

Your question only shows JSON, so I'm not sure if this is about JSON serialization or Kotlin. Since it's tagged kotlin, I'm assuming you're already deserializing the initial list to Kotlin with something like this
data class NamedThing(val id: Int, val name: String)
val list: List<NamedThing> = TODO("somehow you're getting a list of those here")
If you already have this, you can easily create a map from this list using:
val map = list.associate { it.id to it.name }

Related

Kotlin - switching object detail based on group by

For example I have a class with below json format
[
{
"name": "a",
"detail": [
"1",
"2",
"3"
]
},
{
"name": "b",
"detail": [
"2",
"3",
"4"
]
}
]
how to change grouped it based on the detail?
[
{
"detail": "1",
"name": [
"a"
]
},
{
"detail": "2",
"name": [
"a",
"b"
]
},
{
"detail": "3",
"name": [
"a",
"b"
]
},
{
"detail": "4",
"name": [
"b"
]
}
]
below is my class structure
data class funName(
#field:JsonProperty("name")
val name: String = "",
#field:JsonProperty("detail")
val detail: Array<String> = arrayOf(""),
)
and my object is based on the array of funName
val data: Array<funName> = ...
i really have no idea how to do it.
val convert = data.groupBy { x -> x.detail } ??
Is this doable in kotlin/java?
Since the original data is grouped by name, you can think of the original data as a list of pairs
name detail
a 1
a 2
a 3
b 2
b 3
b 4
Mapping it to this format first would make it very easy to group by the second thing (detail) in the pair.
Since each funName corresponds to multiple pairs like this, you should use flatMap on data.
val result = data.flatMap { funName ->
funName.detail.map { funName.name to it }
}
.groupBy(keySelector = { (name, detail) -> detail }, valueTransform = { (name, detail) -> name })
// or more concisely, but less readable
// .groupBy({ it.second }) { it.first }
This will get you a Map<String, List<String>>.
If you want a List<Result>, where Result is something like
data class Result(
val detail: String = "",
val names: List<String> = listOf(),
)
You can add an additional map:
.map { (k, v) -> Result(k, v) }

flatten list with repeated element in kotlin

I have the following data class as a data transfer object.
data class Continent(
val continent: String,
val countries: List<String>
)
so the JSON response is like below.
{
"content": [
{
"continent": "Europe",
"countries": [
"France",
"Germany"
]
}
]
}
However, what I want to do is this:
{
"content": [
{
"continent": "Europe",
"country": "France"
},
{
"continent": "Europe",
"country": "Germany"
}
]
}
I suppose there must be some kotlin collection functions to apply.
Since the dto i wrote in the first code block is unable to be moderated, i have to apply collection functions on the service layer and make response dto accordingly.
Could you please let me know? Thanks in advance.
data class Country(
val continent: String,
val country: String
)
fun List<ContinentResponse>.toCountries(): List<Country> =
flatMap { r -> r.countries.map { country -> Country(r.continent, country) } }

Type mismatch during Gson deserialize json response with kotlin

I am trying to serialize the json response below, but I am unsure how to do it.
This is the Json my backend returns:
[
{
"title": "Dummy section, should not be seen",
"type": "dummy_test",
"metadata": []
},
{
"title": "Title1",
"type": "categories_products",
"metadata": [
{
"id": "1272"
}
]
},
{
"title": "Title2",
"type": "categories_products",
"metadata": [
{
"id": "996"
}
]
}
]
This is my ExploreItem class:
data class ExploreItem(
#SerializedName("metadata") val metadata: List<Metadata> = listOf(),
#SerializedName("title") val title: String = "",
#SerializedName("type") val type: String = ""
) {
enum class ExploreItemType(val value: String) {
#SerializedName("unknown")
UNKNOWN("unknown"),
#SerializedName("other_companies")
OTHER_COMPANIES("other_companies"),
#SerializedName("categories_products")
CATEGORIES_PRODUCTS("categories_products"),
#SerializedName("popular_categories")
POPULAR_CATEGORIES("popular_categories")
}
}
data class Metadata(
#SerializedName("id") val id: String = ""
)
And now I am trying to serialize it in the repository like this:
Serializer.defaultJsonParser.fromJson(response.body!!.string(),ExploreItem::class.java )
but it doesn't work because it's expecting a list of ExploreItem. How can I rewrite the serializer expression to parse it into a list?
From your error
Type mismatch. Required:List Found:ExploreItem!
Post errors is very important, Gson is telling you that it wants a List and not an object of ExploreItem.
In other words, you are telling to Gson with the call Serializer.defaultJsonParser.fromJson(response.body!!.string(),ExploreItem::class.java )
"Hey Gson, from the string I want an object ExploreItem", and Gson is telling you "Hey my friend, you string start with [ ] for sure it is a list of something and not a single object."
You need to pass in the Serializer.defaultJsonParser.fromJson(response.body!!.string(),List<ExploreItem>::class.java)
P.s: I'm not sure about the Kotlin syntax

Apache Nifi: UpdateRecord replace child values

I'm trying to use UpdateRecord 1.9.0 processor to modify a JSON but it does not replace the values as I want.
this is the source message
{
"type": "A",
"ids": [{
"id": "1",
"value": "abc"
}, {
"id": "2",
"value": "def"
}, {
"id": "3",
"value": "ghi"
}
]
}
and the wanted output
{
"ids": [{
"userId": "1",
}, {
"userId": "2",
}, {
"userId": "3",
}
]
}
I have configured the processor as follows
processor config
Reader:
reader
Schema registry:
schema
writer:
writer
And it works, the output is a JSON without the field 'type' and the ids have the field 'userId' instead 'id' and 'value'.
To fill the value of userId, I defined the replace strategy and the property to replace:
strategy
But the output is wrong. The userId is always filled with the id of the last element in the array:
{
"ids": [{
"userId": "3"
}, {
"userId": "3"
}, {
"userId": "3"
}
]
}
I think the value of the expression is ok because if I try to replace only one record it works fine (/ids[0]/userId, ..id)
Nifi docs has a really similar example (example 3):
https://nifi.apache.org/docs/nifi-docs/components/org.apache.nifi/nifi-standard-nar/1.7.1/org.apache.nifi.processors.standard.UpdateRecord/additionalDetails.html
But it does not work for me.
What am I doing wrong?
thanks
Finally I have used JoltJSONTransform processor instead UpdateRecord
JoltJSONTransform
template:
[
{
"operation": "shift",
"spec": {
"ids":{
"*":{
"id": "ids[&1].userId"
}
}
}
}
]
Easier than UpdateRecord

Filter nested list with kotlin

Ques: I want to filter list within a list. All of my data models are immutable.
My JSON structure looks like this
{
"root": [
{
"id": 2,
"val": 1231.12,
"fruit": [
{
"id": 2,
"name": "apple"
}
]
},
{
"id": 3,
"val": 1231.12,
"fruit": [
{
"id": 2,
"name": "apple"
},
{
"id": 3,
"name": "orange"
}
]
}
],
"fruits": [
{
"id": 1,
"name": "apple"
},
{
"id": 2,
"name": "guava"
},
{
"id": 3,
"name": "banana"
}
]
}
Problem Statement - Basically, I want to create a list of all items of root where fruit name is apple. Currently, my naive solution looks like this. This involves creating a temporary mutuable list and then add specific items to it.
Below solution works fine but is there any other better way to achieve the same.
val tempList = arrayListOf<RootItem>()
root?.forEach { item ->
item.fruit.filter {
// filter condition
it.id != null && it.name == "apple"
}
testList.add(item)
}
A combination of filter and any will do the work:
val result = root?.filter { item ->
item.fruits.any { it.id != null && it.name == "apple" }
}
BTW: Your solution will not work. The filter function does return a new list, that you are not using. And you always add the item to the list, not only if the predicate returns true.