Idiomatic way to map single object - kotlin

Is this the idiomatic way to perform a short-circuiting search and map the result to a Boolean?
val foos = mutableListOf<Foo>()
...
fun fooBar(bar: Bar) = if (null != foos.find { it.bar == bar }) true else false
Basically, I was looking for something along the lines of
fun Any?.exists() = null != this
fun fooBar(bar: Bar) = foos.find { it.bar == bar }.exists()
which seems like a useful pattern for anything that might return null.
EDIT:
I settled on writing a simple extension function similar to filterIsInstance():
inline fun <reified R> Iterable<*>.findIsInstance(): R? {
for (element in this) if (element is R) return element
return null
}
Example usage:
val str = list.findIsInstance<String>() ?: return

I believe you are looking for any, which returns true if any of the elements match the given predicate, and is short-circuiting
fun fooBar(bar: Bar) = foos.any { it.bar == bar }

Related

how can I delete When statement on this code (Kotlin)

val customerInfo = when {
visitor.isCustomer -> customerService.getCustomerInfo(visitorId )
else -> null
}
In this Code, visitor.isCustomer is Boolean ( true / false)
Now then, I don't like specify else -> null into the code.
so i want to delete when statement and convert other ways..
How can I do that?
(I prefer to convert it with StandardKt (like let, apply, also... ))
You can just use an if/else
val customerInfo = if (visitor.isCustomer) customerService.getCustomerInfo(visitorId) else null
You could do something like
val customerInfo = vistorId.takeIf { visitor.isCustomer }?.let { customerService.getCustomerInfo(it) }
But I think a when or if statement is cleaner and more readable.
I think JetBrains coding convention would recommend an if statement instead of a when statement here.
Hope this will be more readable.
Without any additional things,
val customerInfo = if (visitor.isCustomer) customerService.getCustomerInfo(visitorId) else null
With your own extension functions
2)Without infix: (condition).ifTrueElseNull{ return value}
inline fun <T> Boolean?.ifTrueElseNull(block: () -> T): T? {
if (this == true) {
return block()
}
return null
}
var a = visitor.isCustomer.ifTrueElseNull{customerService.getCustomerInfo(visitorId)}
With infix: (condition) ifTrueElseNull{ return value}
inline infix fun <T> Boolean?.ifTrueElseNull(block: () -> T): T? {
if (this == true) {
return block()
}
return null
}
var a = visitor.isCustomer ifTrueElseNull{customerService.getCustomerInfo(visitorId)}

Kotlin ? vs ?.let {}

Consider this nice utility extension function i wanted to use :
inline infix fun <T> T?.otherwise(other: () -> Unit): T? {
if (this != null) return this
other()
return null
}
It could be very useful for logging stuff when expressions evaluated to null for example:
val x: Any? = null
x?.let { doSomeStuff() } otherwise {Log.d(TAG,"Otherwise happened")}
but I see that it wont work for :
val x: Any? = null
x?.otherwise {Log.d(TAG,"Otherwise happened")}
see here for running example
Well when thinking about it i guess that makes sense that if x is null the ? makes the postfix not be executed, but i dont understand why the let in the first example is any different?
Is it possible to fix the utility to be more robust and work without having to have let in the chain?
First, you can simplify the implementation:
inline infix fun <T> T?.otherwise(other: () -> Unit): T? {
if (this == null) { other() }
return this
}
Or
inline infix fun <T> T?.otherwise(other: () -> Unit): T? =
also { if (it == null) other() }
When you do this:
null?.otherwise { println("Otherwise happened") }
?. means "execute if not null", so otherwise is not executed.
What you need to write is:
null otherwise { println("Otherwise happened") }
Note this is very similar to the ?: operator (as Vadik pointed out in the comments):
null ?: println("Otherwise happened")
The difference is that otherwise always returns the value on the left (the same as also), but ?: returns the value on the right when the value on the left is null.
In my opinion, otherwise is confusing, especially as it always returns the left value despite the name. You would be better to use the ?: operator. Or perhaps rename it to something like alsoIfNull.
The let example executes because, when you don't utilize the infix feature, it looks like this:
x?.let {}.otherwise {println("1")}
Notice that it's not ?.otherwise; therefore, it always executes.
So to use otherwise without let, you can omit the ?.
x.otherwise { ... }
x?.let { doSomeStuff() }.otherwise {Log.d(TAG,"Otherwise happened")}
// ⬇️
val value = if (x != null) {
doSomeStuff()
} else {
null
}
value.otherwise {Log.d(TAG,"Otherwise happened")}
x?.otherwise { Log.d(TAG,"Otherwise happened") }
// ⬇️
if (x != null) {
otherwise { Log.d(TAG,"Otherwise happened") }
} else {
null
}
?. means if the value is not null then execute the method and return the result otherwise return null

What is the elegant way of for loop with condition to add to a list in Kotlin

What is the more elegant way of doing the following code in Kotlin
fun bar(bars:List<Bar>): List<Foo>{
val foos = mutableListOf<Foo>()
for(bar in bars){
val foo = foo(bar)
if(foo != null){
foos.add(foo)
}
}
return foos
}
fun foo(bar:Bar): Foo?{
if(bar.something){
return null
}
return Foo()
}
bar() can be rewritten to use mapNotNull():
fun bar(bars: List<Bar>) = bars.mapNotNull{ foo(it) }
or (using a method reference):
fun bar(bars: List<Bar>) = bars.mapNotNull(::foo)
And foo() could also be written with an expression body:
fun foo(bar: Bar) = if (bar.something) null else Foo()
(I've omitted the return types too, as the compiler easily infers them — though you may want to keep them for extra safety/readability.)
Both would also work well as extension functions:
fun List<Bar>.bar() = mapNotNull{ it.foo() }
fun Bar.foo() = if (something) null else Foo()
The whole thing can be simplified to:
bars.filterNot { it.something }.map { Foo() }
This is because you are doing two things:
foo returns Foo() if a certain property is false, otherwise returns null
bar filters out the non-null results.
So what you want are Foo objects for every bar where Bar.something is false, which is what this does.
Working example:
class Foo
data class Bar(val something: Boolean)
fun List<Bar>.toFoos(): List<Foo> = filterNot { it.something }.map { Foo() }
fun main() {
val input = listOf(Bar(true), Bar(false), Bar(false), Bar(true), Bar(true))
val output = input.toFoos()
println(output)
}
Output;
[Foo#4a574795, Foo#f6f4d33]
U could use mapNotNull().
val foos = bars.mapNotNull { foo(it) }
Hope its elegant enough.
These two functions could be replaced with the following one-liner:
fun bar(bars:List<Bar>): List<Foo> = Array(bars.count { !it.something }) { Foo() }.asList()
the elegent way is to write it in a functional way :
fun bar(bars: List<Bar>): List<Foo> {
return bars.filter { !it.something }.map { Foo() }
}

Kotlin - Why do we have to explicit type parameter(s) for generic method?

I'm working on extension method like this:
infix fun <T> T.isNullOr(other: T): Boolean {
if (this == null) return true
return this == other
}
and I'm trying to use this method like this.
val thisShouldWork = true isNullOr true // this is true
val thisShouldNotWork = true isNullOr 0 // No compilation errors?
I expected compilation error because type parameter is automatically set to Boolean for isNullOr but it wasn't. What's happening?
am I misunderstanding about it?
in C#, same code working well as I expected.
static bool IsNullOr<T>(this T t, T other) {
if (t == null) return true;
return Equals(t, other);
}
bool howAboutThis = 0.IsNullOr(0);
bool andThis = 0.IsNullOr(false); // error - cannot detect type parameter for this
Here, val thisShouldNotWork = true isNullOr 0 is equal to val thisShouldNotWork: Boolean = true.isNullOr<Any>(0). Type parameter as inferred as the closest parent.
And function's return type is based on logical expression evaluation: this == other. Let's see == function declaration: public open operator fun equals(other: Any?): Boolean. It receives Any?.
Type parameter in this function has nothing to do with Boolean.
Just remember that generic type information is erased at runtime and whenever you try to put something into a method that accepts generics, then the common denominator is assumed, e.g.:
listOf("one", 123) // -> assumes T:Any and therefore gives List<Any>
Now for your example that would mean "one".isNullOr(123) both become Any.
As a sidenote however, if you declare a specific type (e.g. List<String>) as shown next, it will not work to assign a different type to it:
val test : List<String> = listOf(123) // this will not work
It is already known at compile time that the given int can't become a string. This sample however doesn't help you as you do not return that generic type. If your method just looked a bit different, e.g. would have a generic type as return value, it might easily have worked out similar to the List-sample before.
So to fix your sample you need to specify the type which will basically make the infix obsolete, e.g. the following will work as you expect:
val someString : String? = TODO()
val works = someString.isNullOr<String?>("other")
val doesntWork = someString.isNullOr<Int?>(123) // does not nor does:
val doesntWorkToo = someString.isNullOr<String?>(123)
Note that for what you've shown some standard functionality might help you (but not eliminate that specific problem), i.e. using the ?: (elvis operator) with a ?.let:
val someVal : String? = "someString given from somewhere"
val thisWorks = someVal?.let {
it == "some other string to compare"
} ?: true /* which basically means it was null */
val thisWillNot = someVal?.let {
it == 123 // compile error (funny enough: it.equals(123) would work ;-)
} ?: true /* it is null */
I think in this case the generics don't really matter. You only call equals in the method, which you can do on any type. It's basically the same as:
infix fun Any.isNullOr(other: Any): Boolean {
return this == other
}
It compiles without problems because you can always call equals with anything: other: Any?
Thank for answers. I think there is no way to prevent this at compilation level, so I decided to check type for other.
inline infix fun <reified T> T.isNullOr(other: T): Boolean {
if (this == null) return true
if (other !is T) return false
return this == other
}
If you really want to prevent it, you can:
class IsNullOr<T>(val x: T) {
operator fun invoke(other: T): Boolean {
if (x == null) return true
return x == other
}
}
fun <T> T.isNullOr() = IsNullOr(this)
fun main(args: Array<String>) {
val thisShouldWork = true.isNullOr()(true) // compiles
val thisShouldNotWork = true.isNullOr()(0) // doesn't compile
}
This makes type inference depend only on the receiver of isNullOr. If vals could be generic, you'd even keep the original syntax (but they can't).

Component destructuring with fewer than expected components

Let's say I want to do the following:
val (k, v) = pair.split("=".toRegex(), 2)
This code is fine if I always get 2 components from the split - however, if the delimiter is not present in the string, this code throws an exception, because the second element in the array isn't present.
The answer is almost certainly "no", but is there some way to coerce destructure to assign null values to missing components?
When destructuring objects, Kotlin calls componentN() for that object. For arrays, component1() is equal to get(0), component2() is equal to get(1), and so on.
So if the index is out of bounds, it'll throw ArrayIndexOutOfBoundsException, instead of returning null.
But you can make your operator function like this:
operator fun <T> Array<out T>.component1(): T? = if (size > 0) get(0) else null
operator fun <T> Array<out T>.component2(): T? = if (size > 1) get(1) else null
so if I run
val (k, v) = arrayOf(1)
println(k)
println(v)
the output will be
1
null
See:
Destructuring Declarations
You could add your own extension to List that adds the required number of null values to the end:
val (k, v) = pair.split("=".toRegex(), 2).padWithNulls(limit = 2)
Implementation can be done a couple of ways, here's just one:
private inline fun <reified E> List<E>.padWithNulls(limit: Int): List<E?> {
if (this.size >= limit) {
return this
}
val result: MutableList<E?> = this.toMutableList()
result.addAll(arrayOfNulls(limit - this.size))
return result
}
Here's a simpler one as well:
private fun <E> List<E>.padWithNulls(limit: Int): List<E?> {
val result: MutableList<E?> = this.toMutableList()
while (result.size < limit) {
result.add(null)
}
return result
}
Or wrapping this functionality even further:
val (k, v) = pair.splitAndPadWithNulls("=".toRegex(), 2)
private fun String.splitAndPadWithNulls(regex: Regex, limit: Int): List<String?> {
return this.split(regex, limit).padWithNulls(limit)
}
Its working for me
val pair="your string"
if(pair.isNotEmpty()&&pair.contains("=")) {
val (k, v) = pair.split("=".toRegex(), 2)
println(k)
println(v)
}
It doesn't cover as many cases as other answers (also might not be as obvious what's happening) but you can always force there to be at least the correct number of values to destructure (extra values will be ignored). Using your example you can just add null to increase the size of the list returned by split:
val (k, v) = "foo=bar".split("=".toRegex(), 2) + null
> k=foo, v=bar
val (k, v) = "foo".split("=".toRegex(), 2) + null
> k=foo, v=null
Playground example https://pl.kotl.in/W7gGYyAjC