Kotlin equivalent for Optional::map in Java8 - kotlin

Do you know if there is a shortcut for:
if (x == null) null else f(x)
For Java Optional you can just do:
x.map(SomeClass::f)

Kotlin utilizes its own approach to the idea of Option, but there're map, filter, orElse equivalents:
val x: Int? = 7 // ofNullable()
val result = x
?.let(SomeClass.Companion::f) // map()
?.takeIf { it != 0 } // filter()
?: 42 // orElseGet()
I ended up writing a full comparison here:

You can use let in this case, like this:
fun f(x : Int) : Int{
return x+1
}
var x : Int? = 1
println(x?.let {f(it)} )
=> 2
x = null
println(x?.let {f(it)} )
=> null
and as #user2340612 mentioned, it is also the same to write:
println(x?.let(::f)

You can try with let (link to documentation):
x?.let(SomeClass::f)
Example
fun f(n: Int): Int {
return n+1
}
fun main(s: Array<String>) {
val n: Int? = null
val v: Int? = 3
println(n?.let(::f))
println(v?.let(::f))
}
This code prints:
null
4

Related

How can I check if a set of functions all return non null, in a single expression?

Suppose I have three functions foo, bar, baz, all of which return nullable types.
fun foo(): Int? = 1
fun bar(): Int? = 2
fun baz(): Int? = 3
I want to call them, and if all them returns non-null, I want to compute a value from their return values.
I could do this with statements, like this:
val x = foo()
val y = bar()
val z = baz()
val result = if (x != null && y != null && z != null) x + y + z else null
However, I don't like the fact that I have to declare 3 extra variables that I can still access afterwards. By having 3 extra statements like this, it also means that I cannot use expression-bodied functions, if I were writing a function that returns result.
If I use lets instead:
val result = foo()?.let { x ->
bar()?.let { y ->
baz()?.let { z ->
x + y + z
}
}
}
This creates a deep nesting. If it were only one function, this would have been fine, but with 3 functions or more, this makes my intention of "call these three functions, if they are all non null, add them together" rather unclear.
How can I write this in a way that clearly conveys my intention, but also making it a single expression?
If they are of different types, I think you need to write your own helper functions like these (different overloads needed for different numbers of parameters, because there's no other way for the compiler to know the types of the arguments):
inline fun <T : Any, U : Any, R> ifAllNotNull(t: T?, u: U?, block: (t: T, u: U) -> R): R? {
return when {
t != null && u != null -> block(t, u)
else -> null
}
}
inline fun <T : Any, U : Any, V : Any, R> ifAllNotNull(t: T?, u: U?, v: V?, block: (t: T, u: U, v: V) -> R): R? {
return when {
t != null && u != null && v != null -> block(t, u, v)
else -> null
}
}
val result = ifAllNotNull(foo(), bar(), baz()) { x, y, z -> x + y + z }
Note that all three parameters will be evaluated before any are checked for null.
Or if you want to do what you described (hiding the three variables after the result calculation) using just standard library functions, you can use run to limit the scope of the temporary variables:
val result = run {
val x = foo()
val y = bar()
val z = baz()
if (x != null && y != null && z != null) x + y + z else null
}
This would also give you the opportunity to short-circuit if you like:
val result = run {
val x = foo() ?: return#run null
val y = bar() ?: return#run null
val z = baz() ?: return#run null
x + y + z
}
You could filter out all null-values and only apply an operation on the list, if it did not shrink in size, e.g.:
fun sumIfNoneNull(values: List<Int?>): Int? = values
.filterNotNull()
.takeIf { it.size == values.size }
?.sum()
One may generalize this further, e.g.:
fun <T, R> List<T>.foldIfNoneNull(
initial: R,
operation: (acc: R, T) -> R
): R? = this
.filterNotNull()
.takeIf { nonNullList -> nonNullList.size == this.size }
?.fold(initial, operation)
You may use this like any other fold, e.g.:
listOf(foo(), bar(), baz()).foldIfNoneNull(0) { acc, cur -> acc + cur }
val result = listOf(foo(), bar(), baz())
.reduce { acc, i ->
when {
acc == null || i == null -> null
else -> acc + i
}
}
Or as function:
fun <T> apply(operation: (T, T) -> T, vararg values: T?): T? {
return values
.reduce { acc, i ->
when {
acc == null || i == null -> null
else -> operation(acc, i)
}
}
}
val result = apply({ x, y -> x + y }, foo(), bar(), baz())

Is there a function to search in a ArrayList from a position?

I read carefully the ArrayList documentation in Kotlin and apparently there is no way to search a value in ArrayList starting from a pointer. The alternative is write your own function iterating the right elements in ArrayList and testing the condition.
So I've programmed the following code:
fun <T> ArrayList<T>.findNext(cond: (T) -> Boolean, p: Int = 0): Int {
for (i in p..this.lastIndex)
if (cond(this[i])) return i
return -1
}
data class Person (
var name: String,
var age: Int
)
fun main() {
var v = arrayListOf<Person>()
v.add(Person("Paul", 22))
v.add(Person("Laura", 24))
v.add(Person("Paul", 50))
v.add(Person("Mary", 24))
println(v.findNext({it.name=="Paul"})) // 0
println(v.findNext({it.name=="Paul"}, 1)) // 2
println(v.findNext({it.name=="Paul"}, 3)) // -1
}
Is there something better than this?
You can avoid any intermediate collections:
inline fun <T> List<T>.findNext(p: Int = 0, cond: (T) -> Boolean) =
listIterator(p).withIndex().asSequence().find { cond(it.value) }?.let { it.index + p }
By swapping the arguments you can call it like this:
println(v.findNext {it.name=="Paul"}) // 0
println(v.findNext(1) {it.name=="Paul"}) // 2
println(v.findNext(3) {it.name=="Paul"}) // null
fun main() {
var v = arrayListOf<Person>()
v.add(Person("Paul", 22))
v.add(Person("Laura", 24))
v.add(Person("Paul", 50))
v.add(Person("Mary", 24))
println(v.findNext({ it.name == "Paul" },0))//IndexedValue(index=0, value=Person(name=Paul, age=22))
println(v.findNext({ it.name == "Paul" },2))//IndexedValue(index=2, value=Person(name=Paul, age=50))
println(v.findNext({ it.name == "Paul" },3))//null
}
private fun <T> List<T>.findNext(cond: (T) -> Boolean, position: Int): IndexedValue<T>? {
return withIndex().filter { it.index >= position }.firstOrNull { cond(it.value) }
}
maybe use withIndex and a filter ?
val arrayNames = listOf<String>("Paul", "Ann", "Paul", "Roger","Peter")
arrayNames.withIndex().filter {
it.value == "Paul" //value contains the original name
}.forEach{
println(it.index) //indext contains the position.
}
this will give you the output 0 and 2
for your case (person object instead of String) you will use
it.value.name == "Paul"

Method returns tuples in Kotlin

I don't see any examples of how to use tuples in Kotlin.
The errors i get on the first line (method definition) is "unresolved reference: a" and "expecting member declaration" for Int...
private fun playingAround : Pair<out a: Int, out b: Int> {
if(b != 0) {
b = a
a = a * 2
} else {
b = a
a = a * 3
}
return Pair(a, b)
}
About the logic: b is 0 in the beginning and a has a random value.
From the second call on, we go into the else logic.
i don't feel the official doc is enough: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/index.html. I did try also without the ":" after the method name like the official doc seems to imply
-same problem
You are using incorrect syntax. It should be something like this:
private fun playingAround(a: Int, b: Int): Pair<Int, Int> {
val x: Int
val y: Int
if (b != 0) {
y = a
x = a * 2
} else {
y = a
x = a * 3
}
return Pair(x, y)
}
Note that a and b are method parameter values which cannot be reassigned, so you need variables x and y to store the result.
You can write this with much shorter syntax though:
private fun playingAround(a: Int, b: Int) = if (b != 0) Pair(a * 2, a) else Pair(a * 3, a)
Please have a look at the functions chapter of the kotlin reference and/or play around with the Kotlin koans to get familiar with Kotlin (or if, by any means, reading grammar is your favorite, have a look at the function declaration grammar instead; if you do not get what's written there, no problem. Start with the tutorials/reference instead).
One of the solutions could look like this:
private fun playingAround(a: Int, b: Int) = b.let {
if (it != 0) a * 2
else a * 3
} to a
or if you meant, that you actually want to pass a pair, then maybe the following is better:
private fun playingAround(givenPair: Pair<Int, Int>) = givenPair.let { (a, b) ->
b.let {
if (it != 0) a * 2
else a * 3
} to a
}
It's hard to really know what you wanted to accomplish as you didn't really specify what that is.
Extension function instead? For completeness:
private fun Pair<Int, Int>.playingAround() = let { (a, b) ->
b.let {
if (it != 0) a * 2
else a * 3
} to a
}
and of course: you do not need to use let, nor to use to, nor to use destructuring declarations, etc. There are just some of many possible solutions.
You can rewrite your code as the following:
private fun playingAround(a: Int, b: Int) : Pair<Int, Int> {
val tempA: Int
val tempB: Int
if(b != 0) {
tempB = a
tempA = a * 2
} else {
tempB = a
tempA = a * 3
}
return Pair(tempA, tempB)
}
And using Destructuring Declarations you can write the following:
val (a, b) = playingAround(1, 2)
Your function syntax is not correct. I suggest to study the documentation first.
To make this a bit more Kotlin-idiomatic, use if as an expression:
private fun playingAround(a: Int, b: Int): Pair<Int, Int> =
if (b != 0) {
Pair(a * 2, a)
} else {
Pair(a * 3, a)
}

While loop in Kotlin Programming Language

In my kotlin code i am getting Type mismatch(inferred type is Int but Boolean was expected) error.
fun main(args: Array<String>) {
var i = args.size
while (i--){
println(args[i])
}
}
You have to provide a Boolean value as the argument of while. There's no auto-casting of Int to Boolean in Kotlin.
So you can't do while(i--), but you can, for example, do while(i-- != 0) or while(i-- > 0).
Kotlin while loops manual
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y is visible here!
while expects a boolean (true/false), you give an integer (i-1). correct code could be:
fun main(args: Array<String>) {
var i = args.size
while (i>=0){
println(args[i])
i--
}
}

Kotlin: Passing operator as a function parameter

I've the following function in Kotlin
fun evaluate(first:Int?, second:Int?) {
var result = 0
if (v.equals('*')) {
result = (first ?: 0) * (second ?: 0)
} else if (v.equals('+')) {
result = (first ?: 0) + (second ?: 0)
} else if (v.equals('-')) {
result = (first ?: 0) - (second ?: 0)
} else if (v.equals('/')) {
result = (first ?: 0) / (second ?: 0)
}
return result
}
I want to change it in a way so that I can pass as a third parameter needed operator and evalute the expression. Something like
fun evaluate(first:Int?, second:Int?, op: () -> Unit):Int {
return (first ?: 0).op(second ?: 0)
}
How can I pass the operator as a function in this case? I checked the same kind of a question, but it is not clear how you can do that with operator.
Writing a higher order function using a function type as a parameter allows using both built-in operators and lambda expressions for the operation, so this would look like:
fun evaluate(first: Int?, second: Int?, op: (Int, Int) -> Int): Int {
return op(first ?: 0, second ?: 0)
}
Which can be called with built-in operators such as:
val r1 = evaluate(value1, value2, Int::times)
val r2 = evaluate(value1, value2, Int::plus)
val r3 = evaluate(value1, value2, Int::minus)
val r4 = evaluate(value1, value2, Int::div)
And with custom functions:
val r5 = evaluate(value1, value2) { a, b -> (a * a) + b }
Now you can also assign the operators to variables, such as you v:
val v: (Int, Int)->Int = Int::times // typing needed on left to avoid ambiguous alternatives
// and then later...
val r6 = evaluate(value1, value2, v)
Note that a function written for signature Int.(Int)->Int can be passed into a parameter expecting (Int, Int)->Int because the receiver this will be passed in as the first parameter.
Change () -> Unit into Int.(Int) -> Int. Then all the other code should work as you've written it there.
On the calling side it this is the first int, and the first parameter is the second int: { other -> this * other }
you can try do that:
fun evaluate(first: Int?, second: Int? , v:String ): Int = v.op(first ?: 0, second ?: 0)
fun String.op(first:Int,second:Int):Int = when (this) {
"*" -> first * second
"+" -> first + second
//....
else -> throw Exception()
}
fun main(args: Array<String>) {
println(evaluate(2,3,"*"))
println(evaluate(2,3,"+"))
}