Kotlin code duplicates a line in console on its own (looping through a function without taking user input) - kotlin

Let me first put the code then explain:
fun main() {
loop# while (true){
println("Input the action (add, remove, import, export, ask, exit):")
val userInput = scanner.nextLine()
when (userInput){
"add" -> add()
"remove" -> remove()
"import" -> import()
"export" -> export()
"ask" -> ask()
"exit" -> {
print("Bye bye!")
break#loop
}
}
}
}
This is the main function where the user chooses what he wants to do. First he adds some cards that contain terms and definitions as pairs, but thats irrelevant. The problem lies after that, when using the function ask().
fun ask() {
println("How many times to ask?")
for (i in 0 until scanner.nextInt()){
for ((key,value) in map){
println("Print the definition of \u0022$key\u0022:")
var userAnswer = (readLine() ?: "exit").toString()
if (userAnswer == value) {
println("Correct answer.")
continue
}
else {
loop# for ((key2,value2) in map){
if(map.containsValue(userAnswer) && userAnswer == value2){
println("Wrong answer. The correct one is \u0022$value\u0022, you've just written the definition of \u0022$key2\u0022")
break#loop
}else if (!map.containsValue(userAnswer)) {
println("Wrong answer. The correct one is \u0022$value\u0022.")
break#loop
}
}
}
}
}
}
The code works and everything, but after doing the ask() function, just when the program loops back to main(), it prints "Input the action (add, remove, import, export, ask, exit):" twice instead of once. A friend of mine told me it could be an empty input left in cache that triggers this, so that it goes once through main() without actually taking user input.
Please, if anyone has experience with this I would really appreciate it to hear and learn about it and how to prevent it, cause it hinders me from finishing the project. Would changing the way how I take in users input help?

Related

Why is the value not entering the list?

At 'urichecking2' log, I can see there is value. But in 'uriChecking' the uriList is null.
why the uriList.add not work??
private fun getPhotoList() {
val fileName = intent.getStringExtra("fileName")
Log.d("fileNameChecking", "$fileName")
val listRef = FirebaseStorage.getInstance().reference.child("image").child(fileName!!)
var tmpUrl:Uri = Uri.parse(fileName)
Log.d("firstTmpUri","$tmpUrl")
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
tmpUrl = task.result
Log.d("secondTmpUri","$tmpUrl")
Log.d("urichecking2","$task.result")
uriList.add(task.result)
} else {
}
}.addOnFailureListener {
// Uh-oh, an error occurred!
}
}
}
Log.d("thirdTmpUri","$tmpUrl")
Log.d("urichecking", "$uriList")
}
If I do this, the log is output in the order of first, third, and second, and the desired value is in second, but when third comes out, it returns to the value of first.
The listAll method (like most cloud APIs these days, including downloadUrl which you also use) is asynchronous, since it needs to make a call to the server - which may take time. This means the code executes in a different order than you may expect, which is easiest to see if you add some logging:
Log.d("Firebase","Before starting listAll")
listRef.listAll()
.addOnSuccessListener { listResult ->
Log.d("Firebase","Got listResult")
}
Log.d("Firebase","After starting listAll")
When you run this code it outputs:
Before starting listAll
After starting listAll
Got listResult
This is probably not the order you expected, but it perfectly explains why you can't see the list result. By the time your Log.d("urichecking", "$uriList") runs, none of the uriList.add(task.result) has been called yet.
The solution for this is always the same: any code that needs the list result, has to be inside the addOnCompleteListener callback, be called from there, or be otherwise synchronized.
So in its simplest way:
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
uriList.add(task.result)
Log.d("urichecking", "$uriList")
}
}
}
}
This is an incredibly common mistake to make if you're new to programming with asynchronous APIs, so I recommend checking out
Asynchronous programming techniques in the Kotlin language guide
How to get URL from Firebase Storage getDownloadURL
Can someone help me with logic of the firebase on success listener
Why does my function that calls an API or launches a coroutine return an empty or null value?

Kotlin: Code processing without user input

package test
fun main() {
val x= readLine()!!.toInt()
for (i in 1..x) {
repeat(x) { print("#") }
println()
}
}
This is my edited code after your suggestions, stiill, the output remains the same despite my input. It says the source code finished with exit code 0
You answer is already here in the comment of Nikolai Shevchenko, you don't need that TestStringGen object wrapped around the main class, you can't even actually run it at all with it there.
I'm just adding this here as an addition, you don't need the second loop. Since the digit itself represents the number of # you need, you could just take advantage of repeat:
val myNumber = readLine()!!.toInt()
for (digit in 1..myNumber) {
repeat(digit) { print("#") }
println()
}
Also, no need to start the range of the loop from 1 because that will always be an empty line. Unless you need the spacing for whatever reason.

Why can't use continue in let or run

Why it is not allowed to continue from let function?
This code:
fun foo(elements: List<String?>) {
for (element in elements) {
element?.let {
continue // error: 'break' or 'continue' jumps across a function or a class boundary
}
}
}
And even this code:
fun foo(elements: List<String?>) {
loop# for (element in elements) {
element?.let {
continue#loop // error: 'break' or 'continue' jumps across a function or a class boundary
}
}
}
Does not compile with error:
'break' or 'continue' jumps across a function or a class boundary
I know that in this particular case I can use filterNotNull or manual check with smart cast, but my question is why it is not allowed to use continue here?
Please vote for this feature here: https://youtrack.jetbrains.com/issue/KT-1436
These would be called "non-local" breaks and continues. According to the documentation:
break and continue are not yet available in inlined lambdas, but we are planning to support them too.
Using a bare (e.g. non-local) return inside a lambda is only supported if it is an inlined lambda (because otherwise it doesn't have awareness of the context it is called from). So break and continue should be able to be supported. I don't know the reason for the functionality to be delayed.
Note, there are work-arounds for both of them by run either inside or outside the loop, and taking advantage of the fact that at least non-local returns are supported for inline functions.
fun foo(elements: List<String?>) {
run {
for (element in elements) {
element?.let {
println("Non-null value found in list.")
return#run // breaks the loop
}
}
}
println("Finished checking list")
}
fun bar(elements: List<String?>) {
for (element in elements) {
run {
element?.let {
return#run // continues the loop
}
println("Element is a null value.")
}
}
}

nested let blocks in kotlin and valiadation

I want to handle validation in functional approach. I have user validation logic as below
if user object is null throw an exception
if the user is not active throw an exception
if the user type is super do nothing. If user type is admin do some operation.
In my code snippet I am throwing an exception if generated random number is even, otherwise complete the flow.
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
import java.time.Instant
import java.util.UUID
import java.lang.IllegalStateException
import java.util.Random
fun main() {
val user : User? = User(UUID.randomUUID(),Instant.now(),UserStatus.ACTIVE,UserType.SUPER)
//val user : User? = User(UUID.randomUUID(),Instant.now(),UserStatus.ACTIVE,UserType.NORMAL) // works fine
user?.let{ existing -> existing.takeIf{it.status == UserStatus.ACTIVE}?.let{ activeUser ->
activeUser.takeUnless{ user -> user.userType == UserType.SUPER}?.let{
val number = Random().nextInt(5);
println(number)
if(number %2 == 0) throw IllegalStateException("invalid random number")
}
}?: throw IllegalStateException("User is not active right now")
} ?:throw IllegalStateException("user not created at all")
}
data class User(val uuid:UUID, val created:Instant, val status:UserStatus, val userType:UserType)
enum class UserStatus {
ACTIVE,INACTIVE
}
enum class UserType{
SUPER,NORMAL
}
when generated random number is odd its throwing an error message user is not active right now which is not correct. it should silently complete the function call.Any one help me what's wrong with the code?
In the inner block
existing.takeIf { it.status == UserStatus.ACTIVE }?.let { activeUser ->
activeUser.takeUnless { user -> user.userType == UserType.SUPER }?.let {
val number = Random().nextInt(5);
println(number)
if (number % 2 == 0) throw IllegalStateException("invalid random number")
}
} ?: throw IllegalStateException("User is not active right now")
You have a not null value.
Since you're not chaining the ?. calls. The better idea is to do early return/throw.
val existing = user ?: throw IllegalStateException("user not created at all")
So the nested block can be flattened.
The second exception "User is not active right now" happens when one of the two checks is not met.
The programming concept to describe this kind of business logic is not nullable variables. A regular old if check is much cleaner.
if (existing.status != UserStatus.ACTIVE || existing.userType == UserType.SUPER) {
throw IllegalStateException("User is not active right now")
}
BTW your error message seems wrong.
By "functional approach", I think you mean having the code expression-based. But nullable type is not powerful enough for your use case.
You should be looking for Either or Try. Both of them are available in the Arrow library. They are both monads. Their chaining (flatMap), like the ?.let calls, also require the callback hell.
In some languages, there is syntactic sugar to flatten the callback hell of flatMaps. You can take a look at the Monad Comprehensions, and see if you like that.
If not, no problem. In Kotlin, there is no shame associated with early return/throw.
Be pragmatic.

Kotlin Lambda not calling code inside

I encountered the strangest thing.
Lets say I have a text file called "lines.txt". This file contains lines in key value pairs.
test:100
test1:200
test2:300
test3:400
If I read this file in Kotlin the list is not empty however the loop inside the output stream does not get called.
object App {
#JvmStatic
fun main(args: Array<String>) {
// file containing lines of text
val lines = Files.readAllLines(Paths.get("./hashes.txt"))
// not empty
println(lines.size)
// write back a modified version
PrintWriter(FileWriter(File("./lines2.txt"))).use { out -> {
// this doesn't get called
println(lines.size)
lines.forEach {
out.println(it.split(":")[0])
}
}
}
}
}
I don't understand why this is so if anyone can enlighten me that would be awesome.
The list is not empty. A single println(lines.size) will shown you that, because that println is never called.
You simply have one pair of curly braces too much.
change your code to
...
PrintWriter(FileWriter(File("./lines2.txt"))).use { out ->
// list is empty??
println(lines.size)
lines.forEach {
out.println(it.split(":")[0])
}
}
...
The reason is, that a lambda doesn't need its block in curly braces.
So don't write
out -> { ... }
just write
out -> ...
guenther already told you what is wrong with your code, but I think an explanation of what happened is missing.
Consider the following:
val x = { println("y") }
Will it print out y? No, the lamda is never invoked. You have to call x().
Let's take a look at what you did:
val x = { { println("y") } }
x()
Will it print out y? No, because you don't invoke the lambda that prints y.
To make things more clear, let's specify the types explicitely.
val x:() -> (() -> Unit) = { { println("y") } }
Now we can see that the first lambda invoked by x() returns a lambda as well so you would have to call x()() in order to invoke the returned lambda as well.
So using a second pair a curly braces is not just not optional but gives the code a whole new meaning.
But this means that there would be also another solution to your problem.
PrintWriter(FileWriter(File("./lines2.txt"))).use { out -> {
println(lines.size)
lines.forEach {
out.println(it.split(":")[0])
}
}() // <-- add braces here to invoke the lambda
}
So, you can either remove two brackets are add two more. Choice is yours.
Disclaimer: Removing two braces is the way to go. The other option is just to prove a point.