how to clear the list after using - kotlin

This is how I clear list after using, invalidDestination.clear(), but each time after I enter some invalid input, it will shows the previous error message.
for example
1st error message
"add fields abcd"
second time when I enter an invalid data like bcda, it should only return "add fields bcda", but the error message is, I already clear the list, what else should I do?
"add fields abcd, bcda"
private val validationErrors = mutableSetOf<ValidationError>()
private fun validateConfigTypeBduilder(configTypeBuilderList: List<ConfigTypeBuilder>, ruleAttributes: List<String>, destinationFieldList: List<String>) {
if (ruleAttributes.isNotEmpty()) {
var invalidDestination = mutableListOf<String>()
for (destinationField in destinationFieldList) {
if (!ruleAttributes.contains(destinationField)) {
invalidDestination.add(destinationField)
}
}
if (invalidDestination.firstOrNull { invalidDestination.contains(configTypeBuilder.destinationField) } != null)
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $invalidDestination")
invalidDestination.clear()
}
}
private fun addValidationError(fieldPath: String, field: Any, error: String) {
logDataPathValidationError(fieldPath, field, error)
validationErrors.add(
ValidationError(
fieldPath,
error
)
)
}
internal fun logDataPathValidationError(dataPath: String, value: Any?, constraint: String) {
logger.info("{} {} value violates {} constraint", dataPath, value, constraint)
}

Thanks for Eric's inspire, I realized I should add clear destinationFieldList not invalidDestination after the if loop, to make sure next time calling validateConfigTypeBduilder with empty destinationFieldList
destinationFieldList.clear()

Each time you are calling validateConfigTypeBduilder a new list called invalidDestination is created. Then in the first loop you populate that list.
Then inside the if statement (the condition is a little weird the firstOrNull and the null check are irrelevant and removing them will produce the same result) you call addValidationError which logs the message and then clear the list.
If you are calling validateConfigTypeBduilder with the full list destinationFieldList then the number of errors reported will increase, since the list is produced and populated inside the function.
The list is getting cleared properly, but your code stops using it after clearing it.
Instead of sending the whole list addValidationError (assuming the invalidDestination list is ordered) you can send only the last element.
Changing this:
if (invalidDestination.firstOrNull { invalidDestination.contains(configTypeBuilder.destinationField) } != null)
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $invalidDestination")
invalidDestination.clear()
}
to something like this:
invalidDestination.lastOrNull()?.let {
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $it")
}
I don't fully understand you condition in the if statemente, so this is only an aproximation of the changes

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?

How can I place `throw` inside helper-functions but still have null-safety?

I want to wrap a throw in a helper-function, for logging purposes and such.
private fun chooseEmailAddress(user: UserProfile): EmailAddress {
val emailAddress = user.emailAddresses.find {
true // some business logic
}
if (emailAddress == null) {
throwAndNotice(CustomError(
message = "No Email Address found.",
))
}
return emailAddress
}
private fun throwAndNotice(err: CustomError) {
NewRelic.noticeError(err)
throw err
}
The problem:kotlin complains about a type-mismatch:
Type mismatch.
Required: Email
Found: Email?
I guess the compiler does not know that throwAndNotice always throws. If I inline the throwAndNotice method, it works, but it leads to duplication in about a dozen methods.
Is there a way I can tell the compiler "the following method always throws"? Or is there another idiomatic way to deal with this issue? I don't want to resort to !!.
Make it return Nothing. This indicates that it will never return (either throw an exception or infinite loop):
private fun throwAndNotice(err: CustomError): Nothing {
NewRelic.noticeError(err)
throw err
}
You can see other examples of doing this in the standard library, like TODO() and error().
Side note (as mentioned by dey in the comments):
The null check can be rewritten using ?: like this:
return emailAddress ?: throwAndNotice(...)

Simplify testing of a null variable in an IF statement

In Kotlin I have this (which will not compile):
var list: MutableList<String>? = null
if (list.isNotEmpty()) {
}
This will compile:
var list: MutableList<String>? = null
if (list!!.isNotEmpty()) {
}
However, if list is null, a runtime exception will occur. I could do this:
var list: MutableList<String>? = null
if ((list != null) && list.isNotEmpty()) {
}
But this seems to be repetitive everywhere you need to test if something is null. Is there a more eloquent way of doing this in Kotlin?
In the specific case of checking if the list is not null or empty you can use:
if (!list.isNullOrEmpty())
For a list, it's better to avoid handling null state instead handle only empty and non-empty state. refer http://thefinestartist.com/effective-java/43.
Saying that, we don't need to explicitly check for null check and only empty check alone should do the trick.
var list : MutableList<String> = mutableListOf()
list.add("Test1")
list.takeIf { it.isNotEmpty() }?.forEach { println(it) }
We can use
takeIf
to check whether the list is empty or not.
The first way add this line
list = list?:MutableList<String>()
Second way
val isEmpty = list.isEmpty()?:false
if(isEmpty){}else{}
Third way
if (!list.isNullOrEmpty())
as #AndroidDev suggested
Why are getting an error? Since !! require non-null. if the object is null then it will throw NPE
I think most can be done with the safe operator ?. itself. So if you just want to iterate over the list (or reduce, map, or whatever), you can just simply do so directly:
val sumExpression = list?.joinToString("+") { it.someValue } ?: throw exception? use default value?
list?.forEach { println("an entry in the list: $it") } // if there are entries, just iterate over them... if not, there is nothing to do
list?.also {
consume(it) // consume now is only called if the list is not null
}
Enhancing it then with a condition is also rather easy with takeIf or takeUnless (depending on what you require):
list?.takeIf { it.isNotEmpty() }
Now you still have a nullable list and can again call any of the desired functions as shown before:
list?.takeIf( it.isNotEmpty() }?.also {
consume(it)
}
Also a variant instead of list.isNullOrEmpty() (already shown by gpuntos answer) is to compare the possible null value to the expected outcome, e.g.:
if(list?.isNotEmpty() == true) {
// do something with the non-empty list
}
In case it is null the condition simplifies to basically null == true which is false. However I wouldn't overuse that too much as then you don't see the actual nullable types so easily anymore. Such a usage may make sense, if what you are trying to calculate isn't already supported in its own extension function (as is with isNullOrEmpty()), e.g. for entering the if only if the count is 4 it makes sense to use something like: if (list?.count() == 4)...

RxJava2 merge cached data and subject

I have an disk storage that returns an Object? from disk (Could be already saved or not) and an BehaviourSubject (This data comes from other class call, check code below):
Code is:
private val subject: Subject<Optional<Element>> = BehaviorSubject.create()
fun getElements(): Observable<List<Element>> =
Observable.concat(Observable.just(storage.getElement()), subject)
.filter({ it.isPresent })
.take(1)
.flatMapSingle {
Observable.just(it.get())
.flatMapIterable { it.categories }
.toList()
}
fun updateSubject(response: Response) {
storage.save(response.element) //Save element in storage
subject.onNext(response.element.toOptional())
}
My problem is, in other class I do
getElements().subscribe(onElements(), onError());
First time, when storage has null it does nothing, even I've got a breakpoint in subject.onNext(response.element.toOptional()), hoping that onNext will trigger a stream for getElements, but nothing happens.
Second time, when I've already saved in storage the received element (So, storage.getElement() returns something) it works fine.
My functional description is:
Get element from both cache and subject, take first that arrives, and return it (First time it will be who the comes subject one), next time, i'm hoping that first one will be the storage one.
I am assuming that storage is some sort of persistence object so it so storage.getElement() might return a valid object at the time you create the subject?
If that is the case, then I think you should check to see if you have a stored object before you create the subject, if so use BehaviorSubject.createDefalut(storedObject) if it does exist or BehaviorSubject.create() if not.
Then inside your getElements() function I think you can just use subject.filter() ....
Your null optional elements are being filtered out by your .filter { it.isPresent } call so nothing gets emitted downstream.

Why .map on a mutableList doesn't actually change values inside of List in Kotlin?

This is what I have and what I want to achieve:
I have a class which has a mutableList as a field.
I want to find a specific element inside that list and change it.
This is what I've tried so far:
This is the functional statement I was hoping would have worked, after I've also put it in an Extension function:
fun Classroom.setNewParameters(id: String, modifiedName: String) {
this.students.filter { l -> l.id == id }
.map { l -> l.name = modifiedName }
.toList()
}
But the list students appears to be unchanged after this function is called.
I found an "ugly" solution with the code below:
fun Classroom.setNewParameters(id: String, modifiedName: String) {
for (l : Student in this.students) {
if (l.id == id) {
l.name = modifiedName
break
}
}
}
Still tho, I'd really like to know why the first block of code behaves like it does and doesn't actually have any effect on the list.
You can think of map as a way to transform input to get new output. Normally it should not mutate state inside, in other words it should be a function without side effects to pursue maintainability
In your case you clearly want to mutate elements, for that you might want to use that code snippet:
fun Classroom.setNewParameters(id: String, modifiedName: String) {
this.students.filter { l -> l.id == id }
.onEach { l -> l.name = modifiedName }
}
Also, even you used map incorrectly it should must modify field l.name (unless you have implemented you own delegates or getter/setter). Try to debug set breakpoint on this.students.filter { l -> l.id == id } and see if there are any items left after filtering
Noob here but I did just see something related to this the other day.
Is there a reason you wouldn't just check to see if your array contains the old value, return the element id and then assign your new value to that id?
I guess I'm just pointing out that this could be accomplished with a "value in array" type search... but I'm still too new to know the pros and cons of using it vs map.
Kind of like this below, which I got from Kotlin - Idiomatic way to check array contains value
"value" in array
Which translates into the Java API:
array.contains("value")
So I'd check for the old value in the array, return it's index and then reassign that index with the new value.