Kotlin often uses very pragmatic approaches. I wonder whether there is some I don't know of to simplify a filter predicate which just asks for some known values.
E.g. consider the following list:
val list = listOf("one", "two", "2", "three")
To filter out "two" and "2" filtering can be accomplished in several ways, e.g.:
list.filter {
it in listOf("two", "2") // but that creates a new list every time... (didn't check though)
}
// extracting the list first, uses more code... and may hide the list somewhere sooner or later
val toCheck = listOf("two", "2")
list.filter { it in toCheck }
// similar, but probably less readable due to naming ;-)
list.filter(toCheck::contains)
// alternative using when, but that's not easier for this specific case and definitely longer:
list.filter {
when (it) {
"two", "2" -> true
else -> false
}
}
// probably one of the simplest... but not so nice, if we need to check more then 2 values
list.filter { it == "two" || it == "2" }
I wonder... is there something like list.filter { it in ("two", "2") } or any other simple way to create/use a short predicate for known values/constants? In the end that's all I wanted to check.
EDIT: I just realised that the sample doesn't make much sense as listOf("anything", "some", "other").filter { it in listOf("anything") } will always be just: listOf("anything"). However, the list intersection makes sense in constellations where dealing with, e.g. a Map. In places where the filter actually doesn't return only the filtered value (e.g. .filterKeys). The subtraction (i.e. list.filterNot { it in listOf("two", "2") }) however also makes sense in lists as well.
Kotlin provides some set operations on collections which are
intersect (what both collections have in common)
union (combine both collections)
subtract (collections without elements of the other)
In your case, instead of filter, you may use the set operation subtract
val filteredList = list.subtract(setOf("two","2"))
and there you go.
EDIT:
and the fun (pun intended) doesn't end there: you could extend the collections with your own functions such as a missing outerJoin or for filtering something like without or operators i.e. / for intersect
For example, by adding these
infix fun <T> Iterable<T>.without(other Iterable<T>) = this.subtract(other)
infix fun <T> Iterable<T>.excluding(other Iterable<T>) = this.subtract(other)
operator fun <T> Iterable<T>.div(other: Iterable<T>) = this.intersect(other)
Your code - when applied to your example using the intersect - would become
val filtered = list / filter //instead of intersect filter
or - instead of substract:
val filtered = list without setOf("two", "2")
or
val filtered = list excluding setOf("two", "2")
Pragmatic enough?
I ended up with the following now:
fun <E> containedIn(vararg elements: E) = { e:E -> e in elements }
fun <E> notContainedIn(vararg elements: E) = { e:E -> e !in elements }
which can be used for maps & lists using filter, e.g.:
list.filter(containedIn("two", "2"))
list.filter(notContainedIn("two", "2"))
map.filterKeys(containedIn("two", "2"))
map.filterValues(notContainedIn("whatever"))
In fact it can be used for anything (if you like):
if (containedIn(1, 2, 3)(string.toInt())) {
My first approach inspired by Gerald Mückes answer, but with minus instead of subtract (so it only covers the subtraction-part):
(list - setOf("two", "2"))
.forEach ...
Or with own extension functions and using vararg:
fun <T> Iterable<T>.without(vararg other: T) = this - other
with the following usage:
list.without("two", "2")
.forEach... // or whatever...
With the above variant however no infix is possible then. For only one exclusion an infix can be supplied as well... otherwise the Iterable-overload must be implemented:
infix fun <T> Iterable<T>.without(other : T) = this - other
infix fun <T> Iterable<T>.without(other : Iterable<T>) = this - other
Usages:
list without "two"
list without listOf("two", "2")
I don't think there is anything simpler than to create the filtering list/set and then apply it:
val toCheck = listOf("two", "2")
val filtered = list.filter { it in toCheck }
or
val toCheck = setOf("two", "2")
val filtered = list.filter { it in toCheck }
but if you prefer you can create a Predicate:
val predicate: (String) -> Boolean = { it in listOf("2", "two") }
val filtered = list.filter { predicate(it) }
Edit: as for the approach with minus, which is not the case here but has been mentioned, it does not provide simplicity or efficiency since itself is using filter:
/**
* Returns a list containing all elements of the original collection except the elements contained in the given [elements] collection.
*/
public operator fun <T> Iterable<T>.minus(elements: Iterable<T>): List<T> {
val other = elements.convertToSetForSetOperationWith(this)
if (other.isEmpty())
return this.toList()
return this.filterNot { it in other }
}
(from Collections.kt)
Related
Let's say I have the following code in Kotlin:
val min = listOf("hello", "", "teeeeeest").minBy { it.length }
What I understand from the implementation of minBy is that it tracks minValue in a variable and iterates through the whole collection and updates it once it finds an even smaller element.
In the case of Strings though, we know that no element can have a value smaller than 0, therefore the empty String "" is optimal and the iteration can be stopped.
Is there a way I can tell minBy (or maxBy) the optimal value so it can stop once that is reached? If not, how can I implement this most easily?
There's no function in the stdlib that can do this, but you can implement it as an extension function yourself.
By using the non-local return feature of inline lambda functions in Kotlin, you can implement it like this:
fun <T, E : Comparable<E>> Iterable<T>.minBy(theoreticalMinimum: E, keySelector: (T) -> E): T? =
minBy {
val key = keySelector(it)
if (key <= theoreticalMinimum) return it // Non-local return.
else key
}
Now you can use it like this, and it will never visit "teeeeeest":
val min = listOf("hello", "", "teeeeeest").minBy(theoreticalMinimum = 0) { it.length }
In Kotlin I can:
val (specificMembers, regularMembers) = members.partition {it is SpecificMember}
However to my knowledge I can not do something like:
val (specificMembers as List<SpecificMember>, regularMembers) = members.partition {it is SpecificMember}
My question would be - is there's an idiomatic way to partition iterable by class and typecast it those partitioned parts if needed.
If you require that functionality more often, you may just reimplement the actual partition according to your needs, e.g.:
inline fun <reified U : T, T> Iterable<T>.partitionByType(): Pair<List<U>, List<T>> {
val first = ArrayList<U>()
val second = ArrayList<T>()
for (element in this) {
if (element is U) first.add(element)
else second.add(element)
}
return Pair(first, second)
}
with a usage similar as to follows:
val (specificMembers, regularMembers) = members.partitionByType<SpecificMember, Member>()
// where specificMembers : List<SpecificMember>
// and regularMembers : List<Member> for this example
Note that this way you can also set the second type to a more generic one. I leave that up to you whether this makes sense. At least this way an unchecked cast isn't necessary.
The alternative is also shown by Simon with the let-usage. You can also directly cast the result of partition (without let and another Pair) to whatever fits, e.g.:
val (specificMembers, regularMembers) = members.partition {it is SpecificMember} as Pair<List<SpecificMember>, List<Member>>
The partition function will return a Pair<List<T>, List<T>> with T being the generic type of your Iterable. You can transform the partitioned values again using e.g. let:
val (specificMembers, regularMembers) = lists
.partition { it is SpecificMember }
.let { Pair(it.first as List<SpecificMember>, it.second) }
I’m trying to use Kotlin’s when block to look up an element in different maps. After confirming the element exists, the code subsequently does not smart-cast the resulting lookup in the map to not null.
Below is a minimum working example: is it possible to rework it such that !! is not needed?
fun main(args: Array<String>) {
val string = "abc"
val map1 = mapOf('a' to 5)
val map2 = mapOf('b' to 4)
when (val char = string.firstOrNull()) {
null -> println("Nothing to find")
in map1 -> println("Found in map1: ${map1[char]!!+1}")
in map2 -> println("Found in map2: ${map2[char]!!-1}")
else -> println("Unrecognised character $char")
}
}
Unfortunately, in Kotlin, functions can't have contracts of the form "if f returns true, then g doesn't return null." Hence, the compiler doesn't use information about definitely successful contains calls.
The workaround with !! is OK in this case because you can be sure that get returns not null. Implementation of complex patterns in when (KT-186) would cover this use case by allowing declaring a variable inside when clauses and providing static guarantees that it's not null.
I have come across the concept called destructuring declarations - when you can return multiple values from a function at once. It seems very convenient, but at the same time it looks like a tricky workaround. Each time when I think about that feature in Java, I understand that it's a hole in my architecture - there should probably be a class then, not just a couple of variables.
What do you think?
The concept allows having classes that clearly identify a few of their primary properties, the components.
Then you can access these components by using a destructuring declaration, without syntactic noise of accessing the properties.
Compare:
val point = clickEvent.getPointOnScreen()
val x = point.xCoordinate
val y = point.yCoordinate
// Use `x` and `y` in some calculations
and, assuming that the type has component1 and component2, just:
val (x, y) = clickEvent.getPointOnScreen()
Basically, it is not necessary to use this sort of syntactic sugar, and the concept itself does not harm any of the abstractions, it only provides a convenient way to access properties of a class instance in some cases when you don't need the instance itself.
Another example is working with map entries, e.g:
for ((key, value) in myMap) { /* ... */ }
There's still a Map.Entry<K, V> behind the (key, value) destructuring, and you can replace it by for (entry in myMap) ..., but usually it's the two properties that you need. This is where destructuring saves you from a little syntactic noise.
You can also define componentN function as extension for non data classes like this:
operator fun Location.component1() = latitude
operator fun Location.component2() = longitude
and when you want to process on list of locations, you can write this:
for ((lat, lon) in locations) {
......
}
What's the point of destructuring declarations in Kotlin?
Structuring, or construction, is creating an object from values in different variables. Destructuring is the opposite, to extract values into variables from within an existing object.
Part of the Kotlin philosophy is to be concise since the simpler and more concise the code is, the faster you’ll understand what’s going on. Destructuring improves readability which is part of being concise. Compare the following two snippets (let's consider the class Triple)
Without using destructuring
fun getFullName() = Triple("Thomas", "Alva", "Edison")
val result = getFullName()
val first = result.first
val middle = result.second
val last = result.third
Using destructuring
fun getFullName() = Triple("Thomas", "Alva", "Edison")
val (first, middle, last) = getFullName()
It is also possible to take advantage of destructuring to extract key and value from Map's entries.
for ((key, value) in aMap) {
/* ... */
}
Destructuring is the most useful when dealing with built-in data structures. Their fields have names making sense in the context of a data structure (handy when you're writing your own hashmap), but completely cryptic when you're dealing with the data contained there (which is 100% of the time, nobody writes their own hashmaps). Eg. Pair with it's first and second or Map.Entry with key and value.
Consider transforming Map values:
val myMap = mapOf("apples" to 0, "oranges" to 1, "bananas" to 2)
myMap
.asIterable()
.filter { it.value > 0 }
.sortedBy { it.key.length }
.joinToString(prefix = "We have ", postfix = " in the warehouse") {
"{$it.value} of ${it.key}"
}
To make it readable, you'd have to define intermediate variables:
myMap
.asIterable()
.filter {
val count = it.value
count > 0
}
.sortedBy {
val fruit = it.key
fruit.length
}
.joinToString(prefix = "We have ", postfix = " in the warehouse") {
val count = it.value
val fruit = it.key
"$count of $fruit"
}
Now it's readable, but at what cost?!?
Destructuring makes this cost more beareable:
myMap
.asIterable()
.filter { (fruit, count) -> count > 0 }
.sortedBy { (fruit, count) -> fruit.length }
.joinToString(prefix = "We have ", postfix = " in the warehouse") { (fruit, count) ->
"$count of $fruit"
}
That's the point.
Let f() return a nullable value.
What I want to do is that
if f() is null, get an empty list,
else if f() is not null, get a list of the single item value.
In Scala, we can do something like this:
Option(f()).toList
or more verbosely
Option(f()).map(v => List(v)).getOrElse(List.empty)
In Kotlin, there is no Option (assuming no Funktionale library), and null does not have toList() unlike (None: Option) in Scala.
We have the Elvis operator, but null will be inside the listOf() function, so it will be
listOf(f() ?: /* What can I do here? */)
What we want for null is listOf(/*no argument */), but the Elvis operator requires an argument, so listOf(f() ?: ) will result in a compile error.
At least we can do
val v = f()
if (v == null) listOf() else listOf(v)
but it is a two liner.
Is there some expression for this?
Where I will use this expression is in the class's primary constructor default argument, so if it is not a one liner, it will be enclosed in brackets, so something like this:
class A(
val p1: List<V> = run {
val v = f()
if (v == null) listOf() else listOf(v)
},
val p2: ... = ...,
...)
This looks pretty ugly, isn't it?
EDIT
As #Naetmul pointed out, listOfNotNull(f()) is syntactically better to what I originally posted below, and also takes a variable number of arguments, for example
val myList = listOfNotNull(f(), g(), h())
will return a list of all the results that were not null.
I would use let here.
val myList = f()?.let { listOf(it) } ?: emptyList()
Use a ?. safe call on the return value of f(), then use let to run a code block. If f() is null, it won't run this block of code, resulting in a null value. Then we use the ?: elvis operator to fall back to an empty list.
Here it is broken up into several lines for a better understanding
val myValue = f()
val myList: List<Any>
if (myValue != null) {
myList = listOf(myValue)
} else {
myList = emptyList()
}
For this specific question, I can do
listOfNotNull(f())