how to set multiple kotlin variables in one line - kotlin

I want to fill two variables in the same line, but I don't know the best way to do it at kotlin
var a:String? = null
var b:String? = null
a, b = "Text"

Not possible in Kotlin (unless you are ready to resort to some contrived constructs with repetition as described in other answers and comments). You cannot even write
a = b = "Text"
because weirdly enough, assignments are not expressions in Kotlin (as opposed to almost everything else like if, return, throw, swicth, etc., which are expressions in Kotlin, but not in Java, for example).
So, if you want to assign exactly the same value without repetition (of the assigned value), you'll have to write
a = "Text"
b = a
Note, that there is also an also function (pun intended), so technically you can write the following if you really want to stay on one line
a = "Text".also { b = it }
but I doubt it is really worth it.

var a: String? = null; var b: String? = null
or
var (a: String?, b: String?) = null to null
But please don't ever do so

Simply create an inline array, iterate through and assign values.
arrayListOf(a, b, c, d).forEach { it = "Text" }

Related

Idiomatic way of comparing results of two completableFutures with assertj

There are 2 completable futures cf1 and cf2 defined as follows:
CompletableFuture<Boolean> cf1 = CompletableFuture.completedFuture(true);
CompletableFuture<Boolean> cf2 = CompletableFuture.completedFuture(true);
Technically, one could do:
var result1 = cf1.get();
var result2 = cf2.get();
assertThat(result1).isEqualTo(result2);
For example, if there was only one future, we could do the following:
assertThat(cf1)
.succeedsWithin(Duration.ofSeconds(1))
.isEqualTo(true);
Is there a more idiomatic way to compare the two futures against each other? Note that while the example here uses CompletableFuture<Boolean>, the Boolean can be replaced with any class.
If you are interested only in value comparison, passing cf2.get() as argument of isEqualTo should be enough:
CompletableFuture<Boolean> cf1 = CompletableFuture.completedFuture(true);
CompletableFuture<Boolean> cf2 = CompletableFuture.completedFuture(true);
assertThat(cf1)
.succeedsWithin(Duration.ofSeconds(1))
.isEqualTo(cf2.get());
The only downside is that get() can potentially throw ExecutionException and InterruptedException so they need to be declared in the test method signature.
If type-specific assertions are needed, succeedsWithin(Duration, InstanceOfAssertFactory) can help:
assertThat(cf1)
.succeedsWithin(Duration.ofSeconds(1), InstanceOfAssertFactories.BOOLEAN)
.isTrue(); // hardcoded check to show type-specific assertion
What about composing the two futures into one that completes successfully with a value of true if and only if both individual futures complete successfully with the same value? You could e.g. use thenCompose followed by thenApply:
CompletableFuture<Boolean> bothEqual = cf1.thenCompose(b1 -> cf2.thenApply(b2 -> b1 == b2));
If the sequential execution from this solution is problematic, you can parallelize by implementing a helper function alsoApply and use that one instead of thenCompose. See this answer for more details.

How does Kotlin's data class copy idiom look for nullable types?

I have some code which looks like this, where param is of a data class type:
val options = if (param.language == null) {
param.copy(language = default())
} else {
param
}
Now, however, the language object has been moved into a hierarchy of nullable objects, so the check must look like this:
if (param.subObj?.nextObj?.language == null) { ... }
How do I use the copy idiom in this case?
One way to do this is:
val newParam = when {
param.subObj == null -> param.copy(subObj = SubObj(nextObj = NextObj(language = Language())))
param.subObj.nextObj == null -> param.copy(subObj = param.subObj.copy(nextObj = NextObj(language = Language())))
param.subObj.nextObj.language == null -> param.copy(subObj = param.subObj.copy(nextObj = param.subObj.nextObj.copy(language = Language())))
else -> param
}
I agree that this doesn't look very clean but this seems to be the only way to me, because at each step you need to check if the current property is null or not. If it is null, you need to use the default instance otherwise you need to make a copy.
Could you do something like this?
// you could create a DefaultCopyable interface if you like
data class SubObj(val prop1: Double? = null, val nextObj: NextObj? = null) {
fun copyWithDefaults() =
copy(prop1 = prop1 ?: 1.0, nextObj = nextObj?.copyWithDefaults() ?: NextObj())
}
data class NextObj(val name: String? = null) {
fun copyWithDefaults() = copy(name = name ?: "Hi")
}
I think you need a special function because you're not using the standard copy functionality exactly, you need some custom logic to define defaults for each class. But by putting that function in each of your classes, they all know how to copy themselves, and each copy function that works with other types can just call their default-copy functions.
The problem there though is:
fun main() {
val thing = SubObj(3.0)
val newThing = thing.copyWithDefaults()
println("$thing\n$newThing")
}
> SubObj(prop1=3.0, nextObj=null)
> SubObj(prop1=3.0, nextObj=NextObj(name=null))
Because nextObj was null in SubObj, it has to create one instead of copying it. But the real default value for name is null - it doesn't know how to instantiate one with the other defaults, that's an internal detail of NextObj. You could always call NextObj().copyWithDefaults() but that starts to look like a code smell to me - why isn't the default value for the parameter the actual default value you want? (There are probably good reasons, but it might mean there's a better way to architect what you're up to)

Combining Two List in Kotlin with Index

There is a data class as fruits.
data class Fruits(
val code: String, //Unique
val name: String
)
The base list indexed items with boolean variable is as below.
val indexList: MutableList<Boolean> = MutableList(baseFruitList.size) { false }
Now the Favourite Indexed list is as below
val favList: MutableList<Boolean> = MutableList(favFruitList.size) { true}
I want a combined full list which basically has the fav item indicated as true.
Ex:
baseFruitList = {[FT1,apple],[FT2,grapes],[FT3,banana],[FT4,mango],[FT5,pears]}
favList = {[FT2,grapes],[FT4,mango]}
The final index list should have
finalIndexed = {false,true,false,true,false}
How can we achieve in Kotlin, without iterating through each element.
You can do
val finalIndexed = baseFruitList.map { it in favList }
assuming, like #Tenfour04 is asking, that name is guaranteed to be a specific value (including matching case) for a specific code (since that combination is how a data class matches another, e.g. for checking if it's in another list)
If you can't guarantee that, this is safer:
val finalIndexed = baseFruitList.map { fruit ->
favList.any { fav.code == fruit.code }
}
but here you have to iterate over all the favs (at least until you find a match) looking to see if one has the code.
But really, if code is the unique identifier here, why not just store those in your favList?
favList = listOf("FT2", "FT4") // or a Set would be more efficient, and more correct!
val finalIndexed = baseFruitList.map { it.code in favList }
I don't know what you mean about "without iterating through each element" - if you mean without an explicit indexed for loop, then you can use these simple functions like I have here. But there's always some amount of iteration involved. Sets are always an option to help you minimise that

Getting the name of the variable as a string in GD Script

I have been looking for a solution everywhere on the internet but nowhere I can see a single script which lets me read the name of a variable as a string in Godot 3.1
What I want to do:
Save path names as variables.
Compare the name of the path variable as a string to the value of another string and print the path value.
Eg -
var Apple = "mypath/folder/apple.png"
var myArray = ["Apple", "Pear"]
Function that compares the Variable name as String to the String -
if (myArray[myposition] == **the required function that outputs variable name as String**(Apple) :
print (Apple) #this prints out the path.
Thanks in advance!
I think your approach here might be a little oversimplified for what you're trying to accomplish. It basically seems to work out to if (array[apple]) == apple then apple, which doesn't really solve a programmatic problem. More complexity seems required.
First, you might have a function to return all of your icon names, something like this.
func get_avatar_names():
var avatar_names = []
var folder_path = "res://my/path"
var avatar_dir = Directory.new()
avatar_dir.open(folder_path)
avatar_dir.list_dir_begin(true, true)
while true:
var avatar_file = avatar_dir.get_next()
if avatar_file == "":
break
else:
var avatar_name = avatar_file.trim_suffix(".png")
avatar_names.append(avatar_name)
return avatar_names
Then something like this back in the main function, where you have your list of names you care about at the moment, and for each name, check the list of avatar names, and if you have a match, reconstruct the path and do other work:
var some_names = ["Jim","Apple","Sally"]
var avatar_names = get_avatar_names()
for name in some_names:
if avatar_names.has(name):
var img_path = "res://my/path/" + name + ".png"
# load images, additional work, etc...
That's the approach I would take here, hope this makes sense and helps.
I think the current answer is best for the approach you desire, but the performance is pretty bad with string comparisons.
I would suggest adding an enumeration for efficient comparisons. unfortunately Godot does enums differently then this, it seems like your position is an int so we can define a dictionary like this to search for the index and print it out with the int value.
var fruits = {0:"Apple",1:"Pear"}
func myfunc():
var myposition = 0
if fruits.has(myposition):
print(fruits[myposition])
output: Apple
If your position was string based then an enum could be used with slightly less typing and different considerations.
reference: https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_basics.html#enums
Can't you just use the str() function to convert any data type to stirng?
var = str(var)

How to convert Object(with value) into Map

I have a object that I want to print it into string [key1=value1&key2=value2...etc] without the null value key value pair and comma into &.
So first of all i think of putting it into a map but it won't work and I don know how it work either.
val wxPayOrderObj = WxPayOrder(appid = "wx0b6dcsad20b379f1", mch_id =
"1508334851", nonce_str = UUID.randomUUID().toString(),sign = null,
body = "QQTopUp", out_trade_no = "20150806125346", total_fee = req.total_fee,
spbill_create_ip = "123.12.12.123",
trade_type = "JSAPI", openid = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o")
so the output will be
appid=wx0b6dc78d20b379f1&mch_id=150788851&nonce_str=UUID.randomUUID().toString()&
body=QQTopUp&out_trade_no=20150806125346&total_fee=req.total_fee&
spbill_create_ip=123.12.12.123&trade_type=JSAPI&openid=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
anyone please help me, thanks in advances.
I don't really get your question, but you want to convert object to string (to a format that you want)?
Override the object's toString() to return "[key1=value1&key2=value2...etc]"
example
override fun toString(){
// make sure you compute the data first
val answer = "[key1=$value1&key2=$value2...etc]"
return answer
}
The $ is used in string templates (That's directly writing the name of a variable, the value will be used later to be concatenated) with other strings)