Kotlin Comparator Compilation Error in IntelliJ - kotlin

Using Kotlin 1.2.41-release and given a List<Pair<Int, Int>>, the following code generates a compilation error in Intellij, although Gradle command line build works.
sortedWith(compareBy({ it.first }, { it.second }))
Cannot choose among the following candidates without completing type inference.
public fun <T> compareBy(vararg selectors: (???) -> Comparable<*>?): kotlin.Comparator<???> defined in kotlin.comparisons
public fun <T> compareBy(vararg selectors: (Pair<Int, Int>) -> Comparable<*>?): kotlin.Comparator<Pair<Int, Int>> defined in kotlin.comparisons
How can I fix this?
Edit:
edges
.map {
it.either.run {
val p = this
val q = it.other(this)
val min = min(p, q)
if (min == p) p to q else q to p
}
}
.sortedWith(compareBy({ it.first }, { it.second }))
.toList()
where, edges is Iterable<Edge>
class Edge(private val v: Int, private val w: Int, val weight: Double) : Comparable<Edge> {
val either: Int
get() = v
fun other(vertex: Int): Int {
return if (v == vertex) w else v
}
override fun compareTo(other: Edge): Int {
return weight.compareTo(other.weight)
}
override fun toString(): String {
return "Edge(v=$v, w=$w, weight=$weight)"
}
}

Related

Kotlin. Find element by param in nested lists

I have some data clases
data class Data(val docNumber: Int?,
val docType: Int?,
val fiscprops: List<FiscProp>,
val name: String?
) {
fun getFiscProp(tag: Int) = fiscprops.firstOrNull { it.tag == tag }
}
data class FiscProp(val caption: String?,
val printable: String?,
val tag: Int?,
val value: Any?,
val fiscprops: List<FiscProp>?)
I need to find FiscProp by tag in nested lists. If I use getFiscProp I can find FiscProp only if it is on first level of hierarchy.
How can I find element on the all levels? I don't know how many levels will be.
val FiscProp.allProps: Sequence<FiscProp>
get() = sequence {
yield(this#allProps)
fiscprops?.forEach {
yieldAll(it.allProps)
}
}
class Data(...) {
fun getFiscProp(tag: Int) = fiscprops.asSequence()
.flatMap { it.allProps}
.firstOrNull { it.tag == tag }
}
You can use a basic recursive search for this:
fun List<FiscProp>.getFiscProp(tag: Int): FiscProp? {
for (fiscProp in this) {
if (fiscProp.tag == tag)
return fiscProp
val found = fiscProp.fiscprops?.getFiscProp(tag)
if (found != null)
return found
}
return null
}
I think this functional version might work too, but I didn't test it:
fun List<FiscProp>.getFiscProp(tag: Int): FiscProp? = firstNotNullOfOrNull {
if (it.tag == tag) it else it.fiscprops?.getFiscProp(tag)
}
Here's a tail-recursive version.
data class Data(...) {
fun findByTag(tag: Int) = findByTag(fiscprops, tag)
private tailrec fun findByTag(fiscprops: List<FiscProp>, tag: Int): FiscProp? =
if (fiscprops.isEmpty()) null
else {
val fiscprop = fiscprops.first()
if (fiscprop.tag == tag) fiscprop
else findByTag(fiscprops.drop(1) + fiscprop.fiscprops.orEmpty(), tag)
}
}

Add a side-effect to a function in a generic way

How can I write a Kotlin generic function that takes a function as an argument and adds a side-effect to it? For instance,
fun something(one: Int, two: String): String { return "${one}, ${two}" }
fun somethingElse(arg: Array<String>): String { return "${arg}" }
val w1 = wrapped(::something)
w1(42, "hello")
val w2 = wrapped(::somethingElse)
w2(arrayOf("ichi", "ni"))
The following works for functions that take only a single parameter:
fun <A, R> wrapped(theFun: (a: A) -> R): (a: A) -> R {
return { a: A ->
theFun(a).also { println("wrapped: result is $it") }
}
}
To make this work with an arbitrary number of arguments, I'd need some construct that gives me the type of the argument list. Unfortunately, the Function generic can't be used since it takes only one parameter. The following does not compile:
fun <A, R> wrapped(theFun: Function<A, R>): Function<A, R> {
return { args: A ->
theFun(*args).also { println("wrapped: result is ${it}") }
}
}
Or maybe I could use varargs? Does not seem to work with lambdas. Or Kotlin reflection?
Solution using reflection:
class KFunctionWithSideEffect<R>(private val f: KFunction<R>, private val sideEffect: (R) -> Unit) : KFunction<R> by f {
override fun call(vararg args: Any?) = f.call(*args).also { sideEffect(it) }
override fun callBy(args: Map<KParameter, Any?>) = f.callBy(args).also { sideEffect(it) }
}
fun <R> wrapped(theFun: KFunction<R>, sideEffect: (R) -> Unit = { str -> println("wrapped: result is $str") }) =
KFunctionWithSideEffect(theFun, sideEffect)
Usage:
val w1 = wrapped(::something)
w1.call(42, "hello")
val w2 = wrapped(::somethingElse)
w2.call(arrayOf("ichi", "ni"))

Is it possible to pass an argument into a sequence function?

I'm looking for a way to pass an argument into a Kotlin sequence function similar to how it works in JS:
function *gen () {
console.log(yield) // prints 1
console.log(yield) // prints 2
}
const it = gen()
it.next() // first iteration will execute the first yield and pause
it.next(1) // we pass 1 to the first yield which will be printed
it.next(2) // we pass 2 to the second yield which will be printed
Something like this in Kotlin:
fun main() {
val it = gen().iterator()
// Iterator#next() doesn't expect an argument
it.next(1)
it.next(2)
}
fun gen() = sequence {
println(yield(null)) // Would print 1
println(yield(null)) // Would print 2
}
Kotlin Sequences do not support passing arguments to each yield, but you have at least 2 ways to implement needed behaviour:
Using actors:
class NextQuery<A, T>(val arg: A, val next: CompletableDeferred<T> = CompletableDeferred())
fun test() = runBlocking {
val actor = GlobalScope.actor<NextQuery<String, Int>> {
for (nextQuery in channel) {
nextQuery.next.complete(nextQuery.arg.length)
}
}
val query1 = NextQuery<String, Int>("12345")
actor.send(query1)
println(query1.next.await())
val query2 = NextQuery<String, Int>("1234")
actor.send(query2)
println(query2.next.await())
}
Using channels:
class ArgSequenceScope<out A, in T>(
private val argChannel: ReceiveChannel<A>,
private val nextChannel: SendChannel<T>
) {
suspend fun yield(next: T) {
nextChannel.send(next)
}
suspend fun arg(): A = argChannel.receive()
}
class ArgSequence<in A, out T>(
private val argChannel: SendChannel<A>,
private val nextChannel: ReceiveChannel<T>
) {
suspend fun next(arg: A): T {
argChannel.send(arg)
return nextChannel.receive()
}
}
fun <A, T> sequenceWithArg(block: suspend ArgSequenceScope<A, T>.() -> Unit): ArgSequence<A, T> {
val argChannel = Channel<A>()
val nextChannel = Channel<T>()
val argSequenceScope = ArgSequenceScope(argChannel, nextChannel)
GlobalScope.launch {
argSequenceScope.block()
argChannel.close()
nextChannel.close()
}
return ArgSequence(argChannel, nextChannel)
}
fun test() {
val sequence = sequenceWithArg<String, Int> {
yield(arg().length)
yield(arg().length)
}
runBlocking {
println(sequence.next("12345"))
println(sequence.next("1234"))
}
}

Invoking Action by reference in Kotlin

I've a Map of (key, value) where the value is a predefined function.
I want to iterate the input param in the Mp and check where the key is matching with the input parameter, then invoke the equivalent function, something like this
My code required to be something like below:
fun fn1: Unit { // using Unit is optional
println("Hi there!")
}
fun fn2 {
println("Hi again!")
}
fun MainFun(x: int){
val map: HashMap<Int, String> = hashMapOf(1 to fn1, 2 to fn2)
for ((key, value) in map) {
// if key = x then run/invoke the function mapped with x, for example if x = 1 then invoke fn1
}
}
Notes: I read something like below, but could not know how to us them:
inline fun <K, V> Map<out K, V>.filter(
predicate: (Entry<K, V>) -> Boolean
): Map<K, V> (source)
val russianNames = arrayOf("Maksim", "Artem", "Sophia", "Maria", "Maksim")
val selectedName = russianNames
.filter { it.startsWith("m", ignoreCase = true) }
.sortedBy { it.length }
.firstOrNull()
Hi I hope this would help you.
fun fn1() {
println("Hi there!")
}
fun fn2() {
println("Hi again!")
}
fun main(args: IntArray){
val map = hashMapOf(
1 to ::fn1,
2 to ::fn2)
map.filterKeys { it == args[0] } // filters the map by comparing the first int arg passed and the key
.map { it.value.invoke() } // invoke the function that passed the filter.
}
If the keyis RegEx then map.filterKeys { Regex(it).matches(x) } can be used, below full example of it Try Kotlin:
data class Person(val name: String,
val age: Int? = null)
val persons = listOf(Person("Alice"),
Person("Bob", age = 23))
fun old() {
val oldest = persons.maxBy { it.age ?: 0 }
println("The oldest is: $oldest")
}
fun young() {
val youngest = persons.minBy { it.age ?: 0 }
println("The youngest is: $youngest")
}
fun selection(x: String) {
val map = mapOf(
"old|big" to ::old,
"new|young" to ::young)
map.filterKeys { Regex(it).matches(x) }
.map { it.value.invoke() }
}
fun main(args: Array<String>) {
selection("new")
}
fun fn1() {
println("Hi there!")
}
fun fn2() {
println("Hi again!")
}
fun main(args: Array<Int>){
val map = hashMapOf(1 to ::fn1, 2 to ::fn2)
map.forEach { key, function -> function.invoke() }
}
This will do the work but your code does not even have the correct syntax. You should learn the basic first.

Not nullable Mutable Map

Java : 1.8.0_102
Kotlin: 1.0.4
I'm trying to create a map where you can do something like map["key"] += 5 similar to javascript.
Kotlin already has withDefault that solves one part of this, but map's get function still returns a nullable value, so i proceeded to make my own implementation of this inspired by withDefault
interface NonNullableMutableMap<K,V> : MutableMap<K,V> {
override fun put(key: K, value: V): V
override fun get(key: K): V
}
fun <K,V> MutableMap<K,V>.withoutNullValues(default: () -> V): NonNullableMutableMap<K, V> {
return NonNullableMapWrapper(this, default)
}
class NonNullableMapWrapper<K,V>(val map: MutableMap<K,V>, val default: () -> V) : NonNullableMutableMap<K,V> {
override fun put(key: K, value: V): V = map.put(key, value) ?: default()
override fun get(key: K): V {
val value = map.getOrPut(key, default)
return value
}
override val size: Int get() = map.size
override fun containsKey(key: K): Boolean = map.containsKey(key)
override fun containsValue(value: V): Boolean = map.containsValue(value)
override fun isEmpty(): Boolean = map.isEmpty()
override val entries: MutableSet<MutableMap.MutableEntry<K, V>> get() = map.entries
override val keys: MutableSet<K> get() = map.keys
override val values: MutableCollection<V> get() = map.values
override fun clear() {
map.clear()
}
override fun putAll(from: Map<out K, V>) {
map.putAll(from)
}
override fun remove(key: K): V {
return map.remove(key) ?: default()
}
}
I created the following unit test to test it
class NonNullableMapTest {
#Test
fun notNullableTest() {
val map = HashMap<String, Long>().withoutNullValues { 0 }
map["first"] += 10L
map["second"] -= 10L
assertThat(map["first"]).isEqualTo(10L)
assertThat(map["second"]).isEqualTo(-10L)
assertThat(map["third"]).isEqualTo(0L)
}
}
But i'm getting the following error when i run the test:
tried to access method kotlin.collections.MapsKt__MapsJVMKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class foo.bar.NonNullableMapTest
java.lang.IllegalAccessError: tried to access method kotlin.collections.MapsKt__MapsJVMKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class foo.bar.NonNullableMapTest
Any idea how to resolve this issue?
This looks like a bug to me. I recommend reporting it at Kotlin (KT) | YouTrack.
One way to workaround it is by explicitly defining set on your NonNullableMutableMap interface. e.g.:
interface NonNullableMutableMap<K, V> : MutableMap<K, V> {
override fun put(key: K, value: V): V
override fun get(key: K): V
operator fun set(key: K, value: V) {
put(key, value)
}
}
Regarding to the runtime error you get, there is currently a bug in how += operator gets compiled for inline MutableMap.set extension function: https://youtrack.jetbrains.com/issue/KT-14227
The workaround is not to use +=:
map["first"] = map["first"] + 10L