Kotlin map a string to another type? - kotlin

In swift, I can do
"Some String".map { SomeObject($0) }
In kotlin it seems like the string is treated as a char array so the result is the map of each character. Is it possible to get similar behavior like the swift code I posted?
"Some String".map { SomeObject(it) }

You can accomplish something like that with let:
"Some String".let { SomeObject(it) }
If you have an appropriate constructor in place (e.g. constructor(s : String) : this(...)) you can also call it as follows:
"Some String".let(::SomeObject)
run and with work also, but are usually taken if you want to rather call a method of the receiver on it. Using run/with for this would look as follows:
"Some String".run { SomeObject(this) }
with ("Some String") { SomeObject(this) }
// but run / with is rather useful for things like the following (where the shown function calls are functions of SomeObject):
val x = someObject.run {
doSomethingBefore()
returningSomethingElse()
}

Besides using let, run or with, you can also write an extension method:
fun String.toSomeObject() = SomeObject(this)
Then use it like follows:
"SomeObject".toSomeObject()

Related

Kotlin: mark function argument after sanitizing it into a new variable as "do not use this anymore"

To start: this question is already kind of resolved for me. But the discussion might be interesting.
I like code so let's look at this function:
fun foo(path: Path) {
val absPath = path.normalize().absolute() // sanitizing
doSomethingWith(path) // this is unsafe use because path is not sanitized
doSomethingWith(absPath) // this is safe because we are using the sanitized absPath value
}
Kotlin function parameters are always val, therefore we are required to create a new variable if we want to derive from it's value.
We can choose between using a new name or using an old name and annotating it with #Suppress("NAME_SHADOWING") to not get the Name shadowed: ... warning.
I'm looking for something like
fun foo(path: Path) {
val absPath = path.normalize().absolute()
#DoNotUseAnymore path
doSomethingWith(path) // should give a warning/error
doSomethingWith(absPath) // is fine
}
Do you know something like that? Or do you think I'm fiddling around at the wrong end of the equation and should just learn to not feel like doing bad stuff when using the #Suppress-annotation? Since I like to code, this is what I mean:
fun foo(path: Path) {
#Suppress("NAME_SHADOWING")
val path = path.normalize().absolute() // sanitizing
doSomethingWith(path) // there is only one sanitized variable so we are safe
}
In some way this method is the cleanest one... I probably stick to that... Should I publish this question now? Well... maybe :)

Is there any alternative in Kotlin for C#'s AutoResetEvent?

I saw this post where it uses this event and I would like to know if there is any similar alternative in Kotlin, since I would like to use the WaitOne method that it uses for the return.
Example code:
fun exampleMethod(): String{
var command = "example string"
//do someting with this string
return command
}
I want to run this method continuously and receive the value of my variable command of the return every time it runs on the loop.

Pass no value to function with default parameters

I have this Kotlin function:
fun doSomething(user: User = defaultUser) {
//do something
}
and I call it from another place:
val user: User? = getUser()
if (user == null) {
doSomething()
} else {
doSomething(user)
}
Is it possible to improve this code? I think this "if/else" is a little bit messy. Is possible to do something like this?
doSomething(user ?: NoValue)
You can cut it down to user?.run(::doSomething) ?: doSomething() (if doSomething doesn't return null) but I don't know why you'd want to!
Honestly the if/else reads nice to me, stick it on one line without the braces and it's nice and compact. Unfortunately I don't think you can conditionally add parameters into a function call (and handling default parameters can get unnwieldy when you have a few).
I agree with #benjiii, it might be better to have a nullable parameter and handle the default internally, if you don't need to use null as a legit value
You could do something like this:
getUser()?.let { // user is not null
doSomething(it)
} ?: run { // user is null here
doSomething()
}
(cf: Swift 'if let' statement equivalent in Kotlin)
I don't think you could do something shorter without making the code hard to understand Edit 2: Actually you can, see the comment
Edit: I would personally handle the nullable variable inside the function like this:
fun doSomething(user: User?) {
val correctUser = user ?: defaultUser
//do something
}
so you can use the function like this:
doSomething(getUser())
I agree with cactustictacs, just putting it on one line is clear and simple. However, if you use it often and it's bothering you, it's easy enough to wrap it in a function without the default parameter:
fun doSomethingSensibly(user: User?) =
if (user == null)
doSomething()
else
doSomething(user)
Which can be used as:
doSomethingSensibly(getUser())

How do I add multiple copies of the same String to an existing ArrayList?

I have an ArrayList<String>. I want to add n copies of a new String to it.
I've Googled generally and searched on StackOverflow. I've looked at the documentation.
Surely there's a better way than doing a loop?
I was hoping for something like:
myArray.addAll (ArrayList<String>(count: 10, value: "123"))
You can initialize a List with a given size n and an initializer function like this:
fun main() {
val n = 10
val defaultList = List(n) { it -> "default" } // you can leave "it ->" here
println(defaultList)
}
This piece of code then outputs
[default, default, default, default, default, default, default, default, default, default]
If you want to intialize an Array<String> directly without using a List as intermediate, you can do
val defaultArray: Array<String> = Array(n) { "default" }
println(defaultArray.contentToString())
in the main and get the same output (even without the it ->, which, indeed, isn't necessary in this case).

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.