Function returns Emptylist value before inner results are finished in Kotlin with firebase call [duplicate] - kotlin

This question already has answers here:
How to return a list from Firestore database as a result of a function in Kotlin?
(3 answers)
How to return DataSnapshot value as a result of a method?
(6 answers)
Closed 18 days ago.
Trying to retrieve some objects from firebase and manipulate them in this function. However the return of this statement is always an empty list. It skips the databse.child.... action which is the majority of the code and jumps straight to the return statement.
#RequiresApi(Build.VERSION_CODES.O)
fun getFirebaseModuleData(): ArrayList<Module> {
//Firebase Data
var userCloudModules : ArrayList<Module> = ArrayList()
database = Firebase.database.reference
val userId = Firebase.auth.currentUser!!.uid
database.child("Modules").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
var children = it.children
for (child in children){
try {
val value = child.value as HashMap<String, Any>
val name = value["name"] as String
val colour = value["colour"] as String
val sessions = value["moduleSessions"] as List<Map<String, Any>>
val moduleSessions = ArrayList<ModuleSession>()
for (session in sessions) {
val location = session["location"] as String
val sessionType = session["sessionType"] as String
val dayOfTheWeek = session["dayOfTheWeek"] as String
val startTimeData : HashMap<String, String> = session["startTime"] as HashMap<String, String>
val endTimeData : HashMap<String, String> = session["endTime"] as HashMap<String, String>
var startTimeHour = startTimeData["hour"].toString()
var startTimeMinute = startTimeData["minute"].toString()
val startHourLong : Long = startTimeData["hour"] as Long
if(startHourLong < 10){
startTimeHour = "0"+startTimeHour
}
val startMinuteLong : Long = startTimeData["minute"] as Long
if(startMinuteLong < 10){
startTimeMinute = "0"+startTimeMinute
}
val startTime = "$startTimeHour:$startTimeMinute"
var endTimeHour = endTimeData["hour"].toString()
var endTimeMinute = endTimeData["minute"].toString()
val endHourLong : Long = endTimeData["hour"] as Long
if(endHourLong < 10){
endTimeHour = "0"+endTimeHour
}
val endMinuteLong : Long = endTimeData["minute"] as Long
if(endMinuteLong < 10){
endTimeMinute = "0"+endTimeMinute
}
val endTime = "$endTimeHour:$endTimeMinute"
val moduleSession = ModuleSession(location, sessionType, dayOfTheWeek, LocalTime.parse(startTime), LocalTime.parse(endTime))
moduleSessions.add(moduleSession)
}
val module = Module(name,colour,moduleSessions)
userCloudModules.add(module)
} catch (e: Exception) {
Log.e("firebase", "Error casting data", e)
}
}
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
println("Error getting data")
}
return userCloudModules
}
I have done some println's and the data is being retrieved but is not added to the list.

Related

Evaluating a string in kotlin

I wrote a code in kotlin for evaluating a given string from Divison to subtraction, it works but it gives the wrong answer, for example i got a string x in the first line which have answer -215.96666 whereas the answer from code is -237.366666 idk where the error is. i saw more people using stack for doing it, but i wanted to do this way for evaluating a string
var x : String = "5+4-10x20-40/30x20+34/20"
fun div(x: String): String {
val lis1 = x.split("-").toMutableList()
for (ele1 in lis1) {
if ("/" in ele1) {
val pos1 = lis1.indexOf(ele1)
val lis2 = ele1.split("+").toMutableList()
for (ele2 in lis2) {
if ("/" in ele2) {
val pos3 = lis2.indexOf(ele2)
val lis3 = ele2.split("x").toMutableList()
for (ele3 in lis3) {
if ("/" in ele3) {
val pos4 = lis3.indexOf(ele3)
val lis4 = ele3.split("/")
val div = (lis4[0].toDouble() / lis4[1].toDouble()).toString()
lis3[pos4] = div
}
}
lis2[pos3] = lis3.joinToString("x")
}
}
lis1[pos1] = lis2.joinToString("+")
}
}
return (lis1.joinToString("-"))
}
fun mul(x : String) : String{
val lis1 = x.split("-").toMutableList()
for (ele1 in lis1) {
if("x" in ele1){
val pos1 = lis1.indexOf(ele1)
val lis2 = ele1.split("+").toMutableList()
for(ele2 in lis2){
if("x" in ele2){
val pos2 = lis2.indexOf(ele2)
val lis3 = ele2.split("x")
val mul = (lis3[0].toDouble() * lis3[1].toDouble()).toString()
lis2[pos2] = mul
}
}
lis1[pos1] = lis2.joinToString("+")
}
}
return (lis1.joinToString("-"))
}
fun add(x : String): String {
val lis1 = x.split("-").toMutableList()
for(ele1 in lis1){
if("+" in ele1){
val pos1 = lis1.indexOf(ele1)
val lis2 = ele1.split("+")
val add = (lis2[0].toDouble() + lis2[1].toDouble()).toString()
lis1[pos1] = add
}
}
return (lis1.joinToString("-"))
}
fun sub(x : String) : String{
val lis1 = x.split("-").toMutableList()
var sub = 0.0
for(ele1 in lis1){
sub -= ele1.toDouble()
}
return (sub.toString())
}
fun eval(x: String): String {
val divanswer = div(x)
val mulanswer = mul(divanswer)
val addanswer = add(mulanswer)
return sub(addanswer)
}
fun main(args: Array<String>){
println(eval(x))
}
i wanted the answer as -215.96666 but the answer given by code is -237.366666
In the case of this specific input, there are two bugs involved.
First bug is related to the fact you first process + and then -, but in mathematics we should process them together, from left to right. As a result, your algorithm processes this: 1-1+1 (correct answer: 1) as: 1-(1+1) (answer: -1). Alternatively, I believe handling - first and then + should work properly.
Second bug is that in sub() you start from 0 and subtract even the first number, but the first number should be actually added or used as an initial value.
I believe after fixing these two bugs, it should provide a correct answer for this specific input, but there are probably more bugs like these. For example, you incorrectly handle chains of the same operator, you only care about the first pair, so 1+1+1 becomes 2 (and then it is turned to -2 due to earlier mentioned bug).
As a general advice, learn to use a debugger and analyze how your data changes while your code is processing it. Even if you simply print your intermediate results: divanswer, mulanswer and addanswer and then run them through Google for processing, you will notice there is a problem somewhere at the add() step. Then rinse and repeat until everything works correctly.
Thanks to #broot for spotting this, the problem was with the addition and subtraction part, now that i merged them into one and got the correct answers for strings i input, here is the full code corrected.
var x : String = "5+4-10x20-40/30x20+34/20"
fun div(x: String): String {
val lis1 = x.split("-").toMutableList()
for (ele1 in lis1) {
if ("/" in ele1) {
val pos1 = lis1.indexOf(ele1)
val lis2 = ele1.split("+").toMutableList()
for (ele2 in lis2) {
if ("/" in ele2) {
val pos3 = lis2.indexOf(ele2)
val lis3 = ele2.split("x").toMutableList()
for (ele3 in lis3) {
if ("/" in ele3) {
val pos4 = lis3.indexOf(ele3)
val lis4 = ele3.split("/")
val div = (lis4[0].toDouble() / lis4[1].toDouble()).toString()
lis3[pos4] = div
}
}
lis2[pos3] = lis3.joinToString("x")
}
}
lis1[pos1] = lis2.joinToString("+")
}
}
return (lis1.joinToString("-"))
}
fun mul(x : String) : String{
val lis1 = x.split("-").toMutableList()
for (ele1 in lis1) {
if("x" in ele1){
val pos1 = lis1.indexOf(ele1)
val lis2 = ele1.split("+").toMutableList()
for(ele2 in lis2){
if("x" in ele2){
val pos2 = lis2.indexOf(ele2)
val lis3 = ele2.split("x")
val mul = (lis3[0].toDouble() * lis3[1].toDouble()).toString()
lis2[pos2] = mul
}
}
lis1[pos1] = lis2.joinToString("+")
}
}
return (lis1.joinToString("-"))
}
fun final(x: String): Double {
val substr = StringBuffer()
var answer = 0.0
var opr = '+'
for(ele in x.indices){
var current = x[ele]
if(current in '0'..'9' || current == '.'){
substr.append(current)
}
if(current == '-' || current == '+'){
var operand = substr.toString().toDouble()
substr.setLength(0)
when (opr){
'+' -> answer += operand
'-' -> answer -= operand
}
opr = current
}
}
val num = substr.toString().toDouble()
when(opr){
'+' -> answer += num
'-' -> answer -= num
}
return answer
}
fun eval(x: String): Double {
val divanswer = div(x)
return final(mul(divanswer))
}
fun main(args: Array<String>){
println(eval(x))
}

Returning one of different object types from single function in kotlin

I have the following structure at present:
#Entity
#Table(name = "table_app_settings")
data class AppSetting(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "app_setting_id")
val id: Long? = null,
#Column(name = "app_setting_name")
val name: String = "",
#Column(name = "app_setting_value")
var value: String = "",
#Column(name = "app_setting_type")
val type: AppSettingType,
)
enum class AppSettingType {
CHAR,
STRING,
BYTE,
SHORT,
INT,
LONG,
DOUBLE,
FLOAT,
BOOLEAN,
}
This is then saved to the database with the following:
override fun saveAppSetting(setting: AppSetting): DatabaseResult<AppSetting> {
log.info("Saving App Setting ${setting.name} to database.")
return try {
// Attempt to save the entity to the database. If we do not throw an exception, return success.
val savedSetting = appSettingsRepository.save(setting)
DatabaseResult(
code = ResultCode.CREATION_SUCCESS,
entity = savedSetting
)
} catch(exception: DataAccessException) {
log.error("Unable to save App Setting ${setting.name} to database. Reason: ${exception.message}")
DatabaseResult(
code = ResultCode.CREATION_FAILURE
)
}
}
Now, let's say that I wish to save a Char type to database, I figure I would use the following:
override fun saveAppSetting(name: String, value: Char): DatabaseResult<Char> {
val appSettingResult = saveAppSetting(AppSetting(
name = name,
value = value.toString(),
type = AppSettingType.CHAR,
))
return if(appSettingResult.code != ResultCode.CREATION_FAILURE) {
val entity = getAppSetting<Char>(appSettingResult.entity?.name!!).entity.toString().first()
DatabaseResult(
code = appSettingResult.code,
entity = entity
)
} else {
DatabaseResult(
code = ResultCode.CREATION_FAILURE,
)
}
}
I also figured that I would need to do the following in order to retrieve the correct object type:
override fun getAppSetting(name: String): DatabaseResult<Any?> {
log.info("Getting App Setting $name from database.")
val appSetting = appSettingsRepository.findAppSettingByName(name)
return if(appSetting != null) {
log.info("App Setting $name has ID of ${appSetting.id} within the database")
when(appSetting.type) {
AppSettingType.CHAR -> {
DatabaseResult<Char>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.first(),
)
}
AppSettingType.STRING -> {
DatabaseResult<String>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value,
)
}
AppSettingType.BYTE -> {
DatabaseResult<Byte>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toByte(),
)
}
AppSettingType.SHORT -> {
DatabaseResult<Short>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toShort(),
)
}
AppSettingType.INT -> {
DatabaseResult<Int>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toInt(),
)
}
AppSettingType.LONG -> {
DatabaseResult<Long>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toLong(),
)
}
AppSettingType.DOUBLE -> {
DatabaseResult<Double>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toDouble(),
)
}
AppSettingType.FLOAT -> {
DatabaseResult<Float>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toFloat()
)
}
AppSettingType.BOOLEAN -> {
DatabaseResult<Boolean>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toBoolean()
)
}
}
} else {
log.error("App Setting $name does not seem to exist within the database.")
DatabaseResult(
code = ResultCode.FETCH_FAILURE
)
}
However, when I then wish to use said object, I still have to write something like the following:
val newBarcode = getAppSetting("barcode_value").entity.toString().toInt()
Assuming I've "initialised" barcode_value with a value of 177 (for example).
How can I get the function to return what I need without having to do .toString.to...()?
Yes this all possible, here is a simplified demo, firstly
import kotlin.reflect.KClass
data class AppSetting(
val id: Long? = null,
val name: String = "",
var value: String = "",
val type: AppSettingType,
)
enum class AppSettingType(val clazz: KClass<out Any>) {
CHAR(Char::class),
STRING(String::class),
INT(Int::class),
}
So I added a clazz so from the enum we know the Kotlin type
and now a function to simulate your repository fetch
fun findAppSettingByName(name: String): AppSetting? {
return when(name) {
"Char thing" -> AppSetting(value= "C", type = AppSettingType.CHAR)
"String thing" -> AppSetting(value= "Str", type = AppSettingType.STRING)
"Int thing" -> AppSetting(value= "42", type = AppSettingType.INT)
else -> throw IllegalArgumentException()
}
}
Next in the function declaration I have made it generic with T and for the purposes of the demo removed the DatabaseResult container. Then I added a clazz parameter which is the typical Java way of carrying the required class information into the function:
fun <T : Any> getAppSetting(name: String, clazz: KClass<T>): T? {
val appSetting: AppSetting? = findAppSettingByName(name)
return appSetting?.let {
require(clazz == appSetting.type.clazz) {
"appSetting.type=${appSetting.type.clazz} mismatched with requested class=${clazz}"
}
when (appSetting.type) {
AppSettingType.CHAR -> appSetting.value.first()
AppSettingType.STRING -> appSetting.value
AppSettingType.INT -> appSetting.value.toInt()
} as T
}
}
the as T is important to cast the values into the required return type - this is unchecked but the when() clause should be creating the correct types.
Now let's test it:
val c1: Char? = getAppSetting("Char thing", Char::class)
val s1: String? = getAppSetting("String thing", String::class)
val i1: Int? = getAppSetting("Int thing", Int::class)
println("c1=$c1 s1=$s1 i1=$i1")
val c2: Char? = getAppSetting("Char thing")
val s2: String? = getAppSetting("String thing")
val i2: Int? = getAppSetting("Int thing")
println("c2=$c2 s2=$s2 i2=$i2")
}
The output is
c1=C s1=Str i1=42
c2=C s2=Str i2=42
But how do c2/s2/i2 work, the final part is this function
inline fun <reified T : Any> getAppSetting(name: String) = getAppSetting(name, T::class)
This is reified generic parameters... there is no need to pass the clazz because this can be found from the data type of the receiving variable.
There are many articles about this advanced topic, e.g.
https://typealias.com/guides/getting-real-with-reified-type-parameters/
https://medium.com/kotlin-thursdays/introduction-to-kotlin-generics-reified-generic-parameters-7643f53ba513
Now, I didn't completely answer what you wanted because you wanted to receive a DatabaseResult<T> wrapper. What might be possible, is to have a function that returns DatabaseResult<T> and you can obtain the T from it as the "clazz" parameter, but I'll leave that for someone else to improve on :-) but I think that gets you pretty close.

Kotlin: Find the first line of a file that matches a regex, and return captured values

i want get text from a file by using regEx and want save the file with a new name (using the results of the regEx-Find).
My Problem is that i cant get/return the correct genearated (in this example xyz maur) out of the function readFileLineByLineUsingForEachLine(fileName: String) the new newFileName which was generated (sucessfully as expected) in the function.
Line 1 of Source:
start {"Name":"xyz","Civ":"maur","Team":0}
My Prototype:
fun main() {
val f = "./commands.txt";
var newFileName = readFileLineByLineUsingForEachLine(f)
print(newFileName.)
val source = Paths.get(f)
val target = Paths.get("/home/x/snap/0ad/199/.local/share/0ad/replays/0.0.24/2021-03-14_0016/" + newFileName)
// try {
// val move = Files.move(
// source,
// target
// )
// } catch (e: IOException) {
// e.printStackTrace()
// }
};
fun readFileLineByLineUsingForEachLine(fileName: String) // https://www.baeldung.com/kotlin/read-file
= File(fileName).forEachLine lit#{
// "Name":"Cleisthenes"
val regexString = """
"Name":(?<Name>"\w+").*?"Civ":(?<Civ>"\w+").*?"Team":0
""".trim()
var regex = Regex(regexString)
var matched = regex.find(it)?.groupValues
val Name = matched?.get(1)
val Civ = matched?.get(2)
if (Name != null)
println(Name)
if (Civ != null)
println(Civ)
val newFileName = "$Name $Civ"
return#lit
}
Because you want to stop processing as soon as you find a match, I don't think forEachLine is the best choice. Instead you can use useLines, and combine it with first to stop processing once you get a match:
val regex = Regex(""""Name":(?<Name>"\w+").*?"Civ":(?<Civ>"\w+").*?"Team":0""")
fun readFileLineByLineUsingForEachLine(fileName: String) =
File(fileName).useLines { lines ->
val (name, civ) = lines
.map { regex.find(it) }
.filterNotNull()
.first()
.destructured
"$name $civ"
}
For the example you provided, this returns the string "xyz" "maur".
that's just a very little modification of the correct, helpful answer from Adam here https://stackoverflow.com/a/66654710/2891692
fun readFileLineByLineUsingForEachLine2(fileName: String) =
File(fileName).useLines { lines ->
val (name, civ) = lines
.map {
val regexString = """
"Name":(?<Name>"\w+").*?"Civ":(?<Civ>"\w+").*?"Team":0
""".trim()
var regex = Regex(regexString)
regex.find(it)
}
.filterNotNull()
.first()
.destructured
"$name $civ"
}

Cannot 'find' on mutableListOf

The mytable object contains the columns variable (the CollectionColumns class) and the list variable is declared in this class, which contains objects of the Column class.
I try to search in this list by the variable name and don't find it (returns null).
val myTable = Table()
myTable.columns.add("Date")
myTable.columns.add("Name")
myTable.columns.add("Summ")
val find = myTable.columns.find("Summ")
println(find)
result: null
Does anyone know why ?
class Table() {
var columns: CollectionColumns
var rows: CollectionRows
init {
columns = CollectionColumns()
rows = CollectionRows()
}
inner class CollectionColumns() {
var list = mutableListOf<Column>()
fun add(_name: String): Table.Column{
val newColumn = Column(_name)
columns.list.add(newColumn)
for (row in rows.list) {
row.data.put(newColumn.name, null)
}
return newColumn
}
fun find(_name: String): Column?{
val findColumn = columns.list.find { it.name == _name }
return findColumn
}
}
class Column(_name: String) {
val name = _name
}
inner class CollectionRows() {
var list = mutableListOf<Row>()
fun add() : Table.Row {
val newRow = Row()
for (column in columns.list) {
newRow.data.put(column.name, null)
}
rows.list.add(newRow)
return newRow
}
}
class Row() {
var data = mutableMapOf<String, Any?>()
}
}

Convert collection of numbers to range formatted string with Kotlin

I have a very large list of numbers. I need to pass this list of numbers as a URL query parameter. Since these lists can get so large, it could potentially cause the request URL to exceed the allowed length of a URL; also, it's a bit difficult to debug a string of sequential numbers (E.G. 1,2,3,..,500,782). To remedy these issues, I would like to convert the sequential number list string to one that is formatted using a range notation (E.G. -5..-3,1..500,782). How do I create this range notation string using Kotlin and how do I parse the string back to a collection of numbers also using Kotlin?
This will convert a Collection<Int> to a string that uses the "range notation" specified:
fun Collection<Int>.toRangesString(): String {
if (this.isEmpty()) {
return ""
}
if (this.size <= 2) {
return this.toSortedSet().joinToString(",")
}
val rangeStrings = mutableListOf<String>()
var start: Int? = null
var prev: Int? = null
for (num in this.toSortedSet()) {
if (prev == null) {
start = num
prev = num
continue
}
if (num != (prev + 1)) {
_addRangeString(rangeStrings, start!!, prev)
start = num
prev = num
continue
}
prev = num
}
if (start != null) {
_addRangeString(rangeStrings, start, prev!!)
}
return rangeStrings.joinToString(",")
}
private fun _addRangeString(rangeStrings: MutableList<String>, start: Int, prev: Int) {
rangeStrings.add(
when {
(start == prev) -> start.toString()
((start + 1) == prev) -> "${start},${prev}"
else -> "${start}..${prev}"
}
)
}
...and this will parse those range notated strings into a Set<Int>:
fun parseRangesString(str: String): Set<Int> {
if (str.isBlank()) {
return setOf()
}
val ranges = str.trim().split(",")
val numbers = mutableListOf<Int>()
for (range in ranges) {
if (range.contains("..")) {
val (start, end) = range.split("..")
numbers.addAll(start.toInt()..end.toInt())
continue
}
numbers.add(range.toInt())
}
return numbers.toSet()
}
...and, finally, even better than using a huge collection of numbers, you can use Kotlin's IntRange (or LongRange) class:
fun toIntRanges(str: String): Collection<IntRange> = _toRanges(str, ::_createIntRange)
fun toLongRanges(str: String): Collection<LongRange> = _toRanges(str, ::_createLongRange)
private fun <T : ClosedRange<*>> _toRanges(str: String, createRange: (start: String, end: String) -> T): Collection<T> {
if (str.isBlank()) {
return listOf()
}
val rangeStrs = str.trim().split(",")
val ranges = mutableListOf<T>()
for (rangeStr in rangeStrs) {
if (rangeStr.contains("..")) {
val (start, end) = rangeStr.split("..")
ranges.add(createRange(start, end))
continue
}
ranges.add(createRange(rangeStr, rangeStr))
}
return ranges.toList()
}
private fun _createIntRange(start: String, end: String) = IntRange(start.toInt(), end.toInt())
private fun _createLongRange(start: String, end: String) = LongRange(start.toLong(), end.toLong())