Kotlin apply on String not working as expected - android

I am trying to use all features of kotlin, but seems not of them are working, or may be it's my fault.
So, apply to String not work. Example:
val str = someStr.apply {
toUpperCase()
if (contains("W")) replace("W", "w")
}
Input -> xywz
Output -> xywz
Expected -> XYwZ
Java style:
val str = it.text().toString().toUpperCase()
if (str.contains("W")) str.replace("W", "w")
Input -> xywz
Output -> XYwZ
Expected -> XYwZ
Am I doing something wrong?

Actually apply does not return the value you calculated. You may rather want to use either: run, let or with. Additionally, probably more important, is that you do not specify an else path. That might return you a Unit instead of a value, so you may want to specify what should be returned otherwise. Finally, the calls within those methods are not chained. Calling toUpperCase first doesn't alter anything... It's nearly dead code... So you may want to write something like:
val str = with(someStr) {
toUpperCase().run {
if (contains("W")) replace("W", "w")
else this
}
}
However I just used run/let/with to demonstrate its usage, as you already used apply... your shown Java way is of course way easier in this regard and the simplest possible solution is rather the one TheOperator showed by just omitting your condition in the first place as the replace is case-sensitive by default.

toUpperCase() returns a copy of the string (strings are immutable). So you need to store the returned value, as a stand-alone (not last) statement inside apply() it is lost.
Also, you can't use if without else, if you return an expression. The contains() is not even needed in your case.
What you probably would like to do is call toUpperCase() and replace() directly:
val str = someStr.toUpperCase().replace("W", "w")

Docs of "apply" :
Calls the specified function block with this value as its receiver and
returns this value.
So it returns the original value ("this value").
What you should use is "let" instead:
val str = someStr.let {
it.toUpperCase().replace("W", "w")
}
But then you could just use:
val str = someStr.toUpperCase().replace("W", "w")

Related

kotlin add method, having difficulty understanding the "it" argument

private fun defaultOptions() {
val options = ArrayList<TextView>()
tvoptionone?.let { options.add(0, it) }
}
I am currently using the add(index, element) method in kotlin, However, I don't seem to understand what it represents in the element parameter of the add method.
These are the parameters for the add method I am trying to use
add(index, element)
it is the context object on which you've used the let function.
As you've used it with safe call operator (?.) it would only call let if object is non null.
Using ?.let ensures the lambda to be executed only when the object is non null. ?. ensures that object has to be non null and let makes that object available as it inside the lamda.
Here
tvoptionone?.let { options.add(0, it) }
it is a TextView as tvoptionone is a TextView, and it has a value same as tvoptionone.
In the below code
tvoptionone?.let { options.add(0, it) } }
it refers to tvoptionone
notice that lambda passed to let will be called only when tvoptionone is not null, so here it refers to tvoptionone and its value is not null
Questionmark after variable tvoptionone indicates that this variable can be null. If you write just:
options.add(0, tvoptionone)
and variable happens to be null then the add method will throw an error with wording like param element cannot be null or so.
Keyword let, in this particular example, is kind of a guardian against passing null into add method. If tvoptionone has some value (is not null) then it will be tvoptionone itself. Otherwise add method will not be called at all and compilation error will be avoided.
Scope functions are the ones you run on a value, and provide a function to run using that value. There's two kinds of scope functions in Kotlin - the ones where the value is passed in as a parameter (let, also etc.) and the ones where it becomes this (run, apply etc). They work the same, it just changes how you interact with the value - sometimes one is more convenient or suitable than the other
it is just the default name for the parameter passed in:
// these two are the same thing
name.let { println("Hi $it") }
name.let { it -> println("Hi $it") }
// rename it to something that reads better if you like
personData.firstName.let { name -> println("Hi $name") }
People have mentioned the null-check feature, where you can make the let block only run if the value is non-null:
name?.let { println("Hi $it, looking very not-null today") }
but another use for it is creating a temporary variable. If you have a var, it's possible the value will change while you're using it, so it's common to take a copy of it (so you know your copy won't change):
var importantNumber = 123
if (importantNumber > 100) {
// but it might have just been changed by another thread / coroutine and be < 100!
doThing(importantNumber)
}
var importantNumber = 123
val copy = importantNumber
if (copy > 100) {
// the copy can't change, so we know once we've checked it, it's fine
doThing(copy)
}
since let creates a variable to pass in as a parameter, it's basically making a copy in the same way:
var importantNumber = 123
importantNumber.let { if (it > 100) doThing(it) } // safe because 'it' won't change
and that's super important for nullable vars - if you check they're null, you need to know they'll stay null. Using ?.let guarantees that the value passed in as it will be non-null, because it's a copy that's been null-checked

Combine many Kotlin flows to get just their current value

I'm using the Preference APIs that return flows.
There are cases when I need to combine values from different preferences. I know I can use
flow.combine.
Since the method where I need to combine is a suspend function and the value from the flows do not need to flow anywhere anymore, I could also define something like this:
suspend fun <T> Flow<T>.currentValue(): T {
var res: T? = null
this.collect {
res = it
}
return res!!
}
and then just read one value at at time:
val val1 = flow1.currentValue()
val val2 = flow2.currentValue()
val val3 = flow3.currentValue()
Is there a better way to do it? Or should I just use the combine method?
You don't need to define a currentValue function. There is already a Flow.first() function that returns the first value of the Flow. Since these Flows are designed to always emit an initial value with the current value of the preference, this will work fine.
Side note, your currentValue() function will never return because collect doesn't return until the Flow is complete.
Whether you want to individually call first() on each Flow or combine all of them and call first() on the combined Flow is up to you. I don't think it's any cleaner either way.

what does it. mean in the context of alos operator

I am learning how to use the coroutines in kotlin. looking at some examples in the internet i found that within the context f the also operator the reference
it
is used. i could not find any explanation about the meaning of
it
please provide some brief explanantion about what does "it" mean
when you use the also method, it has 1 parameter.
Think of it in Java kinda like this:
foo.also(int it) {
// do stuff
}
In Kotlin, the it parameter is implicit (sometimes you might want to use it sometimes you don't).
If you want to rename it to something more readable you can
foo.also { newName ->
// do stuff with newName
}
Or just use it like it is
foo.also {
// do stuff with $it
}
So therefore when you are using a method (or a closure/lambda) if it has 1 parameter, then the implicit name of that parameter is always it.
Basically it represents the lambda parameter
let's say you want to perform anything on the variable but do to check the nullity first, you can do it like
var str:String?=null // str is of string type
now you can use it fail safe
str?.let{it:String// youll see like this
// now you can access str as **it**
}
it is the implicit name of a single parameter
For more information about it and this in scoping functions like also

Am I having Kotlin functions return a List correctly?

I am new to Kotlin (coming from Delphi, which is object-oriented Pascal). I just want to ensure I am having functions return List<>s correctly:
Making up an absurdly simple example here:
fun firstTenInts(): List<Int> {
val iList: MutableList<Int> = mutableListOf()
for (i in 1..10)
iList.add(i)
return iList
}
So, my thoughts/questions here are:
Am I correct to use a MutableList within the function and simply return it (even though the function's type is List)?
Do I need to create a local MutableList variable? Do I need any local "list" variable? I am used to (again, Delphi) doing something like:
function firstTenInts: TStringList;
var
i: integer;
begin
result.Clear;
for i := 1 to 10 do
result.Add(IntToStr(i));
end;
which requires no "new" local variable. I simply "work" result which is very similar to Kotlin's return value except that it serves as a local variable of the function's type which can be "worked" throughout the function.
Is there no way to manipulate a Kotlin function's return value other than with another (locally created) variable?
Finally, I can rest assured that any local variables I create are destroyed when the function ends even though I'm "passing them back" - correct?
P.S. I know this is an absurd way to create a List of 10 integers. I am using this only as a framework for the questions/issues I have detailed above. Assume that the returned List will be of unknown size.
(Please do not suggest better ways of creating this list of integers; that is not what I am asking about).
Am I correct to use a MutableList within the function and simply return it (even though the function's type is List)?
Generally that's ok. You do such things if you require a list that can be mutated within the function but from outside you do not want it to be easily mutable (which doesn't mean that it isn't mutable; you could still cast it to a mutable list, i.e. firstTenInts() as MutableList would work and so you could also mutate it again).
Whether you need that mutable list in the function or not actually depends on you. For example just calling listOf(1, 2, 3) will return you a List<Int> immediately, i.e. fun first3Ints() = listOf(1,2,3) will immediately return a List<Int>.
Do I need to create a local MutableList variable? Do I need any local "list" variable? I am used to (again, Delphi) doing something like:
You do not need to, but you can. It's up to you what better suites your needs. This also applies to your local list variable. You do not necessarily need one, even though you will have one nonetheless under the hood. Example:
fun first3Ints() = listOf(1, 2, 3)
translates to something like the following:
fun first3Ints() : List<Int> {
val result = listOf(1, 2, 3)
return result
}
On smaller functions you at least can spare some variable declarations using direct assignments. You can also use something like apply, also, let, run or with to overcome val/var, e.g. (just simulating... all variants can be implemented easier):
fun first3Ints() = arrayListOf().apply {
add(1) // apply allows calling all methods of the receiver directly.. (receiver = arrayListOf...)
add(2)
add(3)
}
fun first2Ints() = arrayListOf().also { // similar to apply, but now there is an 'it' available... you could also name that variable differently, e.g. using .also { result -> result.add(1) }
it.add(1)
it.add(2)
}
Is there no way to manipulate a Kotlin function's return value other than with another (locally created) variable?
So this becomes yes (even though .. technically speaking there will be one)... it is possible, but you need to specify on what you basically want to operate, except for the case where you implement an extension function. Then this within the extension function actually becomes the object you called the function on.
Finally, I can rest assured that any local variables I create are destroyed when the function ends even though I'm "passing them back" - correct?
... yes and no. Yes, you can rest assured that any local variable will be garbage collected when need arises (except you are doing something very special). And there is also the no: you don't know when they will be destroyed. Regarding your returned value it is even more special: you are actually getting only a reference to an object, not the object itself... so somehow you basically get that local variable back. You may want to have a look at JVM / stack and heap and how the objects are referenced and stored...
Your return is right and you can use ArrayList instead of mutableListOf and there will be no problem...
but still I didn't understand your main problem but the main topic you were talking about shows that you want to be sure of using the list as a return and that is right man
There are various ways for creating ArrayList of 10 items (just as an example you given). You can find below code snippet such as example :
// directly returning array list as Kotlin has functionality to define such kind of function just like macros
fun firstTenInts(): List<Int> = arrayListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Or by using standard function apply
fun firstTenInts(): List<Int> = ArrayList<Int>().apply {
for (i in 1..10)
this.add(i)
}
// Or by declaring variable of ArrayList
fun firstTenInts(): List<Int> {
val iList = ArrayList<Int>()
for (i in 1..10)
iList.add(i)
return iList
}
// Or by using standard function with
fun firstTenInts(): List<Int> = with(ArrayList<Int>()) {
for (i in 1..10)
this.add(i)
return#with this
}
Above are the various examples defines how you can do differently (Although sample you provided is also a valid example).

Split space from string not working in Kotlin

Anyone wonder this ? Splitting SPACE (" ") in kotlin is not working, i tried with different regex codes but is not working at all.
Tried with this :
value.split("\\s")[0];
value.split("\\s+")[0];
value.split("\\s++")[0];
Then i came up with solution -> Create java constant class which contains this function and returns string array to your kotlin class.
Is there any other solution for this problem where we can directly achieve this thing?
Solution : As #Edson Menegatti said :
KOTLIN Specific : WORKING
values.split("\\s".toRegex())[0]
Many people suggested this solution : NOT WORKING
values.split(" ")[0]
But in my case it's not working.
Here's an issue between the Java and Kotlin implementation of String.split.
While the Java implementation does accept a regex string, the Kotlin one does not. For it to work, you need to provide an actual Regex object.
To do so, you would update your code as follows:
value.split("\\s".toRegex())[0]
Also, as #Thomas suggested, you can just use the regular space character to split your string with:
value.split(" ")[0]
Final point, if you're only using the first element of the split list, you might want to consider using first() instead of [0] - for better readability - and setting the limit parameter to 2 - for better performance.
You need to use :
.toRegex()
fun main(args: Array<String>) {
val str = "Kotlin com"
val separate1 = str.split("\\s".toRegex())[0]
println(separate1) // ------------------> Kotlin
}
OR
You can also use .split(" ")[0] to achieve result. Like
fun main(args: Array<String>) {
val str = "Kotlin com"
val separate1 = str.split(" ")[0]
println(separate1) // ----------> Kotlin
}
String#split (actually CharSequence#split) can take either a regular expression, or just a string which is interpreted literally. So:
value.split(" ")[0]
does what you want.
If you're only using the first element, it's more efficient to also pass limit = 2. Or, even better, use substringBefore.
Kotlin tries to resolve some issues that Java's String library has. For instance, Kotlin tries to be more explicit.
As a result, the split method takes a normal String and does not use it as a regex internally:
"hello world".split("\\s")[0] //no match
"hello world".split(" ")[0] // => "hello"
To explicitly use the overloaded split function that actually takes a regex, the toRegex() extension can be used (inline fun String.toRegex(): Regex (source)):
"hello world".split("\\s".toRegex())[0]// => "hello"
The following shows another example of Kotlin resolving the confusing String::replaceAll method:
Taken from a KotlinConf presentation of Svetlana Isakova, co-author of “Kotlin in Action”
Single delimiter
val splittedStringList = "123.345-80A".split(".")
println(splittedStringList[0])
Several delimiters
println("123.345-80A".split(".", "-"))
Using regex
println("123.345-80A".split("\\.|-".toRegex()))
Try Kotlin Online
Simply use value.split("\s".toRegex())
1.Splits and iterates all items
value.split("\\s".toRegex()).forEach { item ->
//use item
}
2.Spits and use first item
value.split("\\s".toRegex()).first()
3.Spits and use last item
value.split("\\s".toRegex()).last()

Categories

Resources