Imagine receiving from an endpoint an object like this
data class Foo(
val name: String,
val start: String,
val end: String
)
where the properties start and end are string in the format of "00:00:00" to represent the hours, minutes and second from midnight.
I need to save this object in a Room table but with start and end parsed as Int instead of string.
The solution I resorted is this:
data class Foo(
val name: String,
val start: String,
val end: String,
var startInSeconds: Int = 0,
var endInSeconds: Int = 0
) {
init {
startInSeconds = this.convertTimeInSeconds(this.start)
endInSeconds = this.convertTimeInSeconds(this.end)
}
private fun convertTimeInSeconds(time: String): Int {
val tokens = time.split(":".toRegex())
val hours = Integer.parseInt(tokens[0])
val minutes = Integer.parseInt(tokens[1])
val seconds = Integer.parseInt(tokens[2])
return 3600 * hours + 60 * minutes + seconds
}
}
But I'd like to avoid to parse the start and end every time.
There is a way to parse before inserting?
you can try my solution if you want ,
first, instead of using the same object Foo to store your data , it is better to create another data class as Entity to encapsulate the data.
data class Bar(
val name: String,
val start: String,
val end: String,
val startInSeconds: Int, // <<-- using val to make data read only
val endInSeconds: Int // <<-- using val to make data read only
)
you might need to create a thousand of object depend on your data size, so using companion object seems to be a good idea to parse the data to avoid unnecessary memory allocation .
data class Bar(
val name: String,
val start: String,
val end: String,
val startInSeconds: Int,
val endInSeconds: Int
) {
companion object {
// overloading the invoke function
operator fun invoke(foo:Foo) : Bar {
return Bar(
name = foo.name,
start = foo.start,
end = foo.end,
startInSeconds = convertTimeInSeconds(foo.start),
endInSeconds = convertTimeInSeconds(foo.end)
)
}
private fun convertTimeInSeconds(time: String): Int {
val tokens = time.split(":".toRegex())
val hours = Integer.parseInt(tokens[0])
val minutes = Integer.parseInt(tokens[1])
val seconds = Integer.parseInt(tokens[2])
return 3600 * hours + 60 * minutes + seconds
}
}
}
//how to use
fun insertToDatabase(foo:Foo){
val parsedData = Bar(foo)
dao.insert(parsedData)
}
Related
calculateButton.setOnClickListener {
val panelC = binding.panelCount.text.toString()
val panelS = binding.panelSize.text.toString()
val landS = binding.landSlope.text.toString()
val landSi = binding.landSize.text.toString()
val cit = binding.city.text.toString()
val sun = (1000).toInt()
val air = (1.25).toFloat()
val cel = (25).toInt()
val verim = ((sun * air)/ cel).toString().toDouble()
if (panelC.equals("") || panelS.equals("")|| landS.equals("")|| landSi.equals("")||cit.equals("")){
Toast.makeText(requireContext(),"Alanları Doldurunuz.", Toast.LENGTH_SHORT).show()
}
else{
val action = SignUpCalculateFragmentDirections.actionSignUpCalculateFragmentToDataFragment()
Navigation.findNavController(view).navigate(action)
}
val postMap = hashMapOf<String, Any>()
postMap.put("Panel Sayisi",binding.panelCount.text.toString())
postMap.put("Panel Boyutu",binding.panelSize.text.toString())
postMap.put("Arazi Eğimi",binding.landSlope.text.toString())
postMap.put("Arazi Boyutu",binding.landSize.text.toString())
postMap.put("Şehir",binding.city.text.toString())
postMap.put("date", Timestamp.now())
postMap.put("verim",verim.toString().toDouble())
firestore.collection("Posts").add(postMap).addOnFailureListener {
}.addOnFailureListener {
Toast.makeText(requireContext(),it.localizedMessage,Toast.LENGTH_SHORT).show()
}
There is a calculatePage Fragment Codes. On this page, I am trying to make a yield calculation based on the data I receive from the user. However, I need to add the values that are kept constant in the efficiency calculation, such as "sun", "cel", "air" that I defined in the code. I wrote a random operation there as an example. To see if I can write inside the text I'm trying to print without getting any errors. But the app crashed.
private fun getData(){
firestore.collection("Posts").addSnapshotListener { value, error ->
if (error!=null){
Toast.makeText(requireContext(),error.localizedMessage,Toast.LENGTH_SHORT).show()
}else{
if (value !=null){
if (!value.isEmpty){
val documents = value.documents
for (document in documents){
val pc = document.get("Panel Sayisi") as String
val ps = document.get("Panel Boyutu") as String
val ls = document.get("Arazi Eğimi") as String
val lsi = document.get("Arazi Boyutu") as String
val c = document.get("Şehir") as String
val v = document.get("verim") as Double
val post = Post(pc,ps,ls,lsi,c,v)
postArrayList.add(post)
}
}
}
}
val db = FirebaseFirestore.getInstance()
db.collection("Posts").orderBy("date",Query.Direction.DESCENDING).limit(1)
.get()
.addOnCompleteListener {
val result : StringBuffer = StringBuffer()
if (it.isSuccessful){
for (document in it.result){
result.append(document.data.getValue("verim"))
}
verimText.setText(result)
}
}
On this page, I added the values I defined in my class named 'post' to the postList and added them to Firestore.
data class Post(val pc: String, val ps: String, val ls: String, val lsi: String, val c: String, val v : Double)
#ServerTimestamp
var date: Date? = null
This is my post class
The error is like this: java.lang.NullPointerException: null cannot be cast to non-null
type kotlin.Double
As I explained at the beginning of my question, what I am trying to do is using both the data such as "panelCount", "panelSize" that I get from the user and the "sun", "cel", "air" values that are defined as constants, using the "verimText.setText(result)" in the DataFragment page. I need to show this calculation to the user.
The user enters values such as 'Panel Sayisi', 'Panel Boyutu' that should be used while calculating on the calculation page. I need to show this to the user in verimText using both this data and the 'cel', 'sun', 'air' constants that I wrote in the first code.
PS: verim: 20000 value is the result of hypothetical values that I wrote in the first code. In this part, I need to make a calculation using the other data entered by the user and these constant values and show it in the verimText.
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 } }
I have a data class that is used in Room. At the head of the class there are date fields in the form of a string _dateControl and _timeCreate. There are dateControl and timeCreate fields of type Long in the body of the class. When requesting data via Retrofit, the dateControl and timeCreate fields are initialized independently. But if I try to declare an instance of a class, then these fields(dateControl and timeCreate) are not initialized and equal to 0.
Class
#Entity
class DetailNum(
#SerializedName("id_ndt_res")
#PrimaryKey val id: Int,
#SerializedName("id_pr")
val idEntUser: Int,
#SerializedName("id_parts")
val type: Int,
#SerializedName("dt_controlya")
var _dateControl: String,
#SerializedName("number_part")
var numDetail: String?,
#SerializedName("god_izg")
var yearCreate: Int,
#SerializedName("id_zavods")
var idFactory: Int,
#SerializedName("time_create")
var _timeCreate: String,
#SerializedName("id_result")
var resultControl: Int,
#SerializedName("familiya_i_o_speca")
var fioSpec: String?,
#SerializedName("is_new_part")
var isNew: Int,
#SerializedName("is_from_pto")
var isFromPto: Int,
#SerializedName("is_not_kriterii_01_08_2013")
var isNotKrit01082013: Int,
#SerializedName("isLoaded")
var isLoaded: Int = 0
) {
public fun getSimpleDateStr() = SimpleDateFormat("dd.MM.yyyy").format(Date(dateControl))
val strResultControl: String
get() = resultControl.toString()
var dateControl: Long = 0
set(value) {
val date = SimpleDateFormat("dd.MM.yyyy").parse(_dateControl)
field = date.time
}
var timeCreate: Long = 0
set(value) {
val date = SimpleDateFormat("dd.MM.yyyy HH:mm:ss").parse(_timeCreate)
field = date.time
}
var nameResult:String?=""
fun getFirstCharNameResult() = if(!nameResult.isNullOrEmpty()) nameResult?.get(0)?.toUpperCase() else ""
}
Try inicialize like this
var detailNum = DetailNum(0, 0, 0, DateUtils.getSimpleDateStr(Date()), "", 0, 0, DateUtils.getSimpleDateStr(Date()), 0,"", 0, 0, 0 )
Why are long date fields not initialized in the second case and how to initialize them? I am new in Kotlin, please explain, thanks in advance
Your fields like dateControl and timeCreate are not initialised in the constructor! Their setter don't get called automatically if not by some framework.
If you want them to be set right after the constructor initialisation, you can either:
1) .. set default value immediately
val dateControl: Long = SimpleDateFormat("dd.MM.yyyy").parse(_dateControl).time
2) .. or use init if the operation needs more code or you want to gather inner field initialisations together
class DetailNum(...) {
var dateControl: Long = 0
init {
dateControl = SimpleDateFormat("dd.MM.yyyy").parse(_dateControl).time
...
}
}
More infoirmation about constructors and init you can find here.
BTW: your setters don't use the provided value at all, so actually they are useless.
use lateinit to the variables like below
lateinit var subject: TestSubject
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 */ ))
}
I have been writing an app where at first I declared the class signature as below
data class MClass(val id: Int = 0,val descr: String, val timestamp: Long)
Now a need was created where I must have a custom getter for a field above. How can I write this custom getter? I know that If otherwise I could write something like
data class(){
val id=0
val descr = ""
get() = descr + "append smth"
val timestamp:Long = 0
}
You could do something like below:
data class MClass(val id: Int = 0, private val descr: String, val timestamp: Long) {
val description: String
get() = descr + "append smth"
}
You can make it like that:
data class MClass(val id: Int = 0, private val _descr: String, val timestamp: Long) {
val descr: String
get() = _descr + "append something"
}