What is the most optimized way to compare two ArrayLists of different object type? Let's say I have an Array Lists of Remedies and Adherences and them being:
ObjectA
data class ObjectA(val data: List<Objecta> = emptyList()) {
data class Objecta(
val _id: String,
val remedy_id: String,
val patient_id: String,
val date_created: Long,
val is_ongoing: Boolean?,
val start_date: Long,
val medicine_id: String,
val instruction: String,
val medicine_name: String,
val medicine_type: String,
val end_date: Long,
val repeat_cycle: Int,
val allow_edit: Boolean,
//val schedule: List<Schedule>?,
val is_current: Boolean,
//val medicine: Medicine?,
val price: Float
)
}
ObjectB
data class ObjectB(val data: List<Objectb> = emptyList()) {
data class Objectb(
val _id: String,
val adherence_id: String,
val patient_id: String,
val remedy_id: String,
val alarm_time: String,
val action: String,
val action_time: String,
val dose_discrete: String,
val dose_quantity: Int,
val note: String
)
}
How can I compare both those two array lists "remedyList" and "adherencesList" where remedy_id is the same and remove items in adherencesList when remedy_id is not present in remedyList.
This should work:
Prepare the input
data class Rdata (val r_id:String)
data class Adata (val a_id:String,val r_id:String)
val rList = (1..10).map { Rdata("$it")}
val aList = (5..20).map { Adata("foo", "$it")}
Do the filtering:
val rIds = rList.map { it.r_id }.toSet()
val resultList = aList.filter { it.r_id in rIds}
After this, the resultList contains objects with r_id 5-10. (that is, 11-20 have been removed)
Try this code:
val allRemedies = remedies.flatMap { it.data }.associateBy { it.remedy_id }
val allAdherences = adherences.flatMap { it.data }.associateBy { it.remedy_id }
val allPairs = allAdherences.mapValues { allRemedies[it.key] to it.value }
Playground example
remedies is the List<Remedies> and adherences is the List<Adherences>
First we flatten the List<Remedies> to List<Remedy> and associate each Remedy to its remedy_id. allRemedies is Map<String, Remedy>.
Similarly for the adherences, we prepare a Map<String, Adherence>
Then we map each value of allAdherences map to a pair of Remedy? and Adherence. If there is no Remedy for that remedy_id it will be null.
If all you care about is filtering Adherences that don't have a matching Remedy, you can create a Set of remedy_ids from the Remedy List, and use that to efficiently check if they exist and filter on that.
val validRemedyIds: Set<String> = remedies.data.mapTo(mutableSetOf(), Remedy::remedy_id)
val filteredAdherences = Adherences(adherences.data.filter { it.remedy_id in validRemedyIds })
If you need to match them up later, you should use a map instead of set:
val remediesById = remedies.data.associateBy(Remedy::remedy_id)
And then there are several ways you can combine to compare. One example:
val adherencesToRemedies: List<Pair<Adherence, Remedy>> =
adherences.data.mapNotNull { remediesById[it.remedy_id]?.run { it to this } }
Related
I have this type of array in firebase but how to fetch it and use in kotlin
I was able to get as String but how to get its as a data class Like this
data class Comment(
val uid: String,
val comment: String,
val stamp: Timestamp
)
and here's the code of getting string
var text by remember { mutableStateOf("loading...") }
FirebaseFirestore.getInstance().collection("MyApp")
.document("Something").get().addOnSuccessListener {
text = it.get("Comments").toString()
}
Firebase has a toObject method that can be used to turn your document into a custom object.
db.collection("Comments")
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
val comment = document.toObject<Comment>()
}
}
The Comment data class should also define default values. So, it should be like...
data class Comment(
val uid: String = "",
val comment: String = "",
#ServerTimeStamp val stamp: Date? = null
)
I got ArrayLists with HashMaps represents my entities entities just using this:
val cleanAnswers: List<Answer> = (document.get(FIELD_ANSWERS)
as ArrayList<HashMap<String, Any>>).map {
Answer(
it[FIELD_VARIANT] as String,
it[FIELD_IS_CORRECT] as Boolean
)
}
My entity:
class Answer(val answer: String,
val isCorrect: Boolean) : Serializable
I am using the following function to map a network object to a domain one.
Mapping function
fun getLocationLocalModel(input: LocationSearchResponse): List<Location> {
return input.locations.map { location ->
return#map location.bbox?.let {
Location(
name = location.name,
countryCode = location.countryCode,
north = location.bbox.north,
south = location.bbox.south,
west = location.bbox.west,
east = location.bbox.east
)
}
}.filterNotNull()
}
Network DTOs
data class LocationSearchResponse(
#SerializedName("geonames")
val locations: List<Location>
)
data class Location(val bbox: Bbox?, val countryCode: String, val countryName: String,
val geonameId: Int, val lat: String, val lng: String, val name: String)
Domain Model
#Parcelize
data class Location(val name: String, val countryCode: String, val north: Double, val south: Double, val east: Double, val west: Double) : Parcelable
What I want is to ignore the objects where bbox is null so they are not added to the resulting list of locations.
This function works, but there must be a better/simpler way to do this.
Any help would be appreciated.
As Animesh mentioned in the comments, simply change your statement to mapNotNull -- there's no need to use the return# syntax there:
return input.locations.mapNotNull { loc ->
loc.bbox?.let { bbox ->
Location(loc.name, loc.countryCode, bbox.north, bbox.south, bbox.west, bbox.east)
}
}
Alternatively you could filter first, then use !! to dereference:
return input.locations
.filter { it.bbox != null }
.map { loc->
val bbox = loc.bbox!!
Location(loc.name, loc.countryCode, bbox.north, bbox.south, bbox.west, bbox.east)
}
Personally the former seems more readable to me.
data class Movie(
#SerializedName("movie_name")
val movieName : String
#SerializedName("genres")
val movieGenre : List<String>){}
How can I convert a list to string created every object in this data class
You can use additional property in Movie class to convert list to string, e.g. val genre:
data class Movie(
#SerializedName("movie_name")
val movieName: String,
#SerializedName("genres")
val movieGenre: List<String>
) {
val genre: String
get() = buildString { // builds new string using append() method on each item of movieGenre
movieGenre.forEach { append(it) }
}
}
Here is how to access it:
val movie: Movie = getMoviewFromInternet()
val movieGenre: String = movie.genre
That's achievable in Kotlin via joinToString(separator: String), like this:
data class Movie(
#SerializedName("movie_name")
val movieName : String,
#SerializedName("genres")
val movieGenre : List<String>
) {
val genre: String
// concatenate the items using the separator provided
get() = movieGenre.joinToString(" ")
}
You would use it in a main as follows
fun main() {
val genreList: List<String> = listOf(
"Horror", "Splatter", "Romance", "Comedy", "Mystery", "Adult"
)
val movieName = "A.R. Bitrary"
val movie = Movie(movieName, genreList)
println(movie.genre)
}
which then outputs
Horror Splatter Romance Comedy Mystery Adult
I'm new to kotlin so this maybe a very easy issue to resolve.
What I'm trying to do is filter the json response that I receive using Retrofit2 before I display the images in a grid with a RecyclerView.
instagram.com/explore/tags/{hashtag}/?__a=1&max_id= Using Retrofit2 I'm able to get the data response fine and also display the given url images in a RecyclerView.
I have not been successful in using the filter, map, loops and conditions to remove elements from the Arraylist. I do not understand these to the fullest extent but I have searched looking for solutions and those are what I came apon.
Interface
interface InstagramDataFetcher
{
#GET("tags/{tag}/?__a=1&max_id=")
fun getInstagramData(#Path("tag") hashtag: String) : Call <InstagramResponse>
}
Where I get my response from and also get StringIndexOutOfBoundsException
class InstagramFeedFragment : Fragment()
{
private fun onResponse()
{
val service = RestAPI.retrofitInstance?.create(InstagramDataFetcher::class.java)
val call = service?.getInstagramData("hashtag")
call?.enqueue(object : Callback<InstagramResponse>
{
override fun onFailure(call: Call<InstagramResponse>, t: Throwable)
{
Log.d("FEED", " $t")
}
override fun onResponse(
call: Call<InstagramResponse>, response: Response<InstagramResponse>
)
{
//for ((index, value) in data.withIndex())
if (response.isSuccessful)
{
var data: ArrayList<InstagramResponse.InstagramEdgesResponse>? = null
val body = response.body()
data = body!!.graphql.hashtag.edge_hashtag_to_media.edges
for ((index, value) in data.withIndex())
{
if(value.node.accessibility_caption[index].toString().contains("text") ||
value.node.accessibility_caption[index].toString().contains("person"))
{
data.drop(index)
}
}
recyclerView.adapter = InstagramGridAdapter(data, parentFragment!!.context!!)
}
}
})
}
}
This is my model class
data class InstagramResponse(val graphql: InstagramGraphqlResponse)
{
data class InstagramGraphqlResponse(val hashtag: InstagramHashtagResponse)
data class InstagramHashtagResponse(val edge_hashtag_to_media: InstagramHashtagToMediaResponse)
data class InstagramHashtagToMediaResponse(
val page_info: InstagramPageInfo,
val edges: ArrayList<InstagramEdgesResponse>
)
data class InstagramPageInfo(
val has_next_page: Boolean,
val end_cursor: String
)
data class InstagramEdgesResponse(val node: InstagramNodeResponse)
data class InstagramNodeResponse(
val __typename: String,
val shortcode: String,
val display_url: String,
val thumbnail_src: String,
val thumbnail_resources: ArrayList<InstagramThumbnailResourceResponse>,
val is_video: Boolean,
val accessibility_caption: String
)
data class InstagramThumbnailResourceResponse(
val src: String,
val config_width: Int,
val config_height: Int
)
}
Simply again, I want to just remove elements from the arraylist that match certain things what I don't want. For instance. the "is_video" value that comes from the json. I want to go through the arraylist and remove all elements that have "is_video" as true.
Thanks
If you asking how to filter the list then below is the demo.
You just need to use filter on your data which is an ArrayList. I've tried keeping the same structure for the models so that you can get a better understanding.
fun main() {
val first = InstagramNodeResponse(
title = "first",
is_video = true
)
val second = InstagramNodeResponse(
title = "second",
is_video = false
)
val list: ArrayList<InstagramEdgesResponse> = arrayListOf(
InstagramEdgesResponse(node = first),
InstagramEdgesResponse(node = second)
)
val itemsWithVideo = list.filter { it.node.is_video == true }
val itemsWithoutVideo = list.filter { it.node.is_video == false }
println(itemsWithVideo.map { it.node.title }) // [first]
println(itemsWithoutVideo.map { it.node.title }) // [second]
}
// Models
data class InstagramEdgesResponse(val node: InstagramNodeResponse)
data class InstagramNodeResponse(
val title: String,
val is_video: Boolean
)
I am working on updating the parsing of an API response that uses a Serialized Data Class to parse the JSON response. The serialization works perfectly fine right now, but the new data that I'm attempting to parse into data class is not fully reliant on data in the json. Here is what I mean by that:
The data class is Career, and the new data I need to parse is a set of skills and each have a rating. The json data is very simple and contains the skills as such:
{
// other career data
...
"mathematics_skill": 8,
"critical_thinking_skill": 6
... // the remaining skills
}
Using straight serialization, I would only be able to store the data as such:
data class Career(
// Other career data
#serializableName("mathematic_skill") val mathSkill: Int,
#serializableName("critical_thinking_skill") val mathSkill: Int,
// remaining skills
)
However, I would like to store all skills in an array variable of a custom skills class that not only contains the rating, but also the name of the skill and a color. Basically, when I access the skills data of a career, I would like to access it like such:
val careerMathSkill = career.skills[0]
val mathRating = careerMathSkill.rating
val mathColor = careerMathSkill.color
Is it possible to use the serialized data from the data class to add non-serialized data to the same data class? (Sorry for the weird wording, not sure how else to explain it)
EDIT: Here is what I have:
class CareersRemote(
#SerializedName("careers") val careers: List<Career>
) {
companion object {
fun parseResponse(response: Response<CareersRemote>): CareersResponse {
return if (response.isSuccessful) {
response.body()!!.format()
} else
CareersResponse(listOf(CareersResponse.ErrorType.Generic()))
}
}
fun format(): CareersResponse {
val careers = topCareers.map {
Career(
id = it.id,
title = it.title,
)
}.toMutableList()
return CareersResponse(CareersResponse.SuccessData(careers = careers))
}
data class Career(
#SerializedName("id") val id: String,
#SerializedName("title") val title: String,
)
}
Here is what I am hoping to do in a way
class CareersRemote(
#SerializedName("careers") val careers: List<Career>
) {
companion object {
fun parseResponse(response: Response<CareersRemote>): CareersResponse {
return if (response.isSuccessful) {
response.body()!!.format()
} else
CareersResponse(listOf(CareersResponse.ErrorType.Generic()))
}
}
fun format(): CareersResponse {
val careers = topCareers.map {
Career(
id = it.id,
title = it.title,
)
}.toMutableList()
return CareersResponse(CareersResponse.SuccessData(careers = careers))
}
data class Career(
#SerializedName("id") val id: String,
#SerializedName("title") val title: String,
// skills array that will need to be filled out based on the data I got in the json
var skills: List<Skill>
)
}
EDIT: The suggested solution
class CareersRemote(
#SerializedName("careers") val careers: List<Career>
) {
companion object {
fun parseResponse(response: Response<CareersRemote>): CareersResponse {
return if (response.isSuccessful) {
response.body()!!.format()
} else
CareersResponse(listOf(CareersResponse.ErrorType.Generic()))
}
}
fun format(): CareersResponse {
val careers = topCareers.map {
Career(
id = it.id,
title = it.title,
)
}.toMutableList()
return CareersResponse(CareersResponse.SuccessData(careers = careers))
}
data class Career(
#SerializedName("id") val id: String,
#SerializedName("title") val title: String,
#SerializedName("math_skill") val mathSkill: Int
#SerializedName("other_skill") val mathSkill: Int
) {
var skills: List<Skill> = {
val mathSkill = Skill(name: "Math", rating: mathSkill, color: /**some color*/)
val otherSkill = Skill(name: "Other", rating: otherSkill, color: /**some color*/)
return listOf(mathSkill, otherSkill)
}
}
}
Yes, you can create a custom JsonDeserializer to modify how the JSON is parsed.
Here is a basic example of what that would look like.
class CareerDeserializer : JsonDeserializer<Career> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Career {
val obj = json.asJsonObject
// standard career data
val id = obj.get("id")?.asString
val name = obj.get("name").asString
// making a Skill object
val skill = Skill(
obj.get("mathematic_skill").asInt,
obj.get("critical_thinking_skill").asInt,
obj.get("swimming_skill").asInt
// etc
)
return Career(id, name, skill)
}
}
And make sure to register that within your GsonBuilder.
val gson = GsonBuilder()
.registerTypeAdapter(Career::class.java, CareerDeserializer())
.create()
Note, you'll also have to create a JsonSerializer if you want to go the other way too.
Edit:
However, if you're just looking to change the syntax of how you're accessing that data, you can do something like this.
data class Career(
// Other career data
val mathSkill: Int,
val thinkSkill: Int
// remaining skills
) {
val skills: List<Int>
get() = listOf(mathSkill, thinkSkill)
}
This would give you a skills list back whenever you needed it, and it would be created when you accessed it, so you won't have to worry about the data being out of sync. This would allow you to access your data as such.
career.skills[0] // get the math skill.
And you can take this another step further by adding a get operator to your Career class.
data class Career(
// Other career data
val mathSkill: Int,
val thinkSkill: Int
// remaining skills
) {
...
operator fun get(pos: Int) = skills[pos]
}
Now, you can simply do
career[0] // get the math skill.
Warning, this is dangerous because you're accessing an Array so you could get OutOfBoundsExceptions. Use constants to help you out.
Edit 2:
val skills = {
listOf(Skill("Math", mathSkill, /** some color */ ),
Skill("Other", otherSkill, /** some color */ ))
}