If I have the following array:
[1,1,1,2,2,1,1,1,1,2,2,3]
Is there any built in method in Kotlin which will filter out adjacent elements of the same value, resulting in:
[1,2,1,2,3]
It's important that the order is preserved.
P.S. My actual use case isn't integers, it's an object which implements equals.
I don't think there is a standard function to do this.
But it is easy to build one with mapOrNull:
fun <T : Any> Iterable<T>.removeAdjacent(): List<T> {
var last: T? = null
return mapNotNull {
if (it == last) {
null
} else {
last = it
it
}
}
}
There's a one-line solution, using zipWithNext():
list.zipWithNext().filter{ it.first != it.second }.map{ it.first } + list.last()
That creates a list of pairs of adjacent elements; we then filter out the identical pairs, and take the first of each remaining pair. That will have omitted the last one, so we have to add that in separately.
That works with any element type, using the object's own notion of equality (via its equals() method); this includes nullable types (unlike another answer). And it's stateless so ‘pure’ functional (which you may or may not consider a good thing!).
It handles one-element lists, but not empty lists; for completeness, you'd have to handle those separately. And it would fit very neatly into an extension function:
fun <T> List<T>.compress() = when (isEmpty()) {
true -> listOf()
else -> zipWithNext().filter{ it.first != it.second }.map{ it.first } + last()
}
Functional solution using fold:
val result = listOf(1,1,1,2,2,1,1,1,1,2,2,3)
.fold(mutableListOf<Int>()) { currentList, currentItem ->
if (currentList.isEmpty()) { // Applies only to the very first item
mutableListOf(currentItem)
} else {
if (currentItem != currentList.last()) {
currentList.apply { add(currentItem) }
} else {
currentList
}
}
}
Related
Every so often, I find myself wanting to compute a value for some sort of filter operation, but then wanting to use that value when it's already disappeared into the condition-checking thing.
For instance:
val found = list.firstOrNull { slowConversion(it).isWanted() }
if (found != null) {
something(found, slowConversion(found))
}
or
when {
other_conditions -> other_actions
list.any { it.contains(regex1) } -> something(list.firstOrNull { it.contains(regex1) } ?: "!!??")
}
For the slowConversion() I can work with a sequence mapped to pairs, although the terms first and second kinda confuse things a bit...
val pair = list.asSequence().map { it to slowConversion(it) }.firstOrNull { it.second.isWanted() }
if ( pair != null ) {
something(pair.first, pair.second)
}
or if I only want the conversion,
val converted = list.firstNotNullOfOrNull { slowConversion(it).takeIf { it.isWanted() } }
but the best I can come up with to avoid the when duplication involves moving the action part into the condition part!
fun case(s: List<String>, r: Regex) {
val match = s.firstOrNull { it.contains(r) }?.also { something(it) }
return match != null
}
when {
other_conditions -> other_actions
case(list, regex1) -> true
}
At this point, it seems I should just have a stack of function calls linked together with ||
other_things || case(list, regex1) || case(list, regex2) || catchAll(list)
Is there something better or more concise for either of these?
You can write your first example like this:
for(element in list) {
val result = slowConversion(element)
if(result.isWanted()) {
something(element, result)
break
}
}
This might not look very Kotlin-ish, but I think it's pretty straightforward & easy to understand.
For your second example, you can use the find function:
when {
other_conditions -> other_actions
else -> list.find { it.contains(regex1) }?.let(::something)
}
If you have multiple regexes, just iterate over them,
val regexes = listOf(regex1, regex2, ...)
for(regex in regexes) {
val element = list.find { it.contains(regex1) } ?: continue
something(element)
break
}
I often stumble upon this problem but don't see a common implementation: how do I idiomatically (functionally) find an element, stop search after the match, and also return a different type (i.e. map whatever matched to another type)?
I've been able to do a workaround with
fun <F,T> Sequence<F>.mapFirst(block: (F) -> T?): T? =
fold(AtomicReference<T>()) { ref, from ->
if (ref.get() != null) return#fold ref
ref.set(block(from))
ref
}.get()
fun main() {
Files.list(someDir).asSequence().map { it.toFile() }.mapFirst { file ->
file.useLines { lines ->
lines.mapFirst { line ->
if (line == "123") line.toInt() else null
}
}
}?.let { num ->
println("num is $num") // will print 123 as an Int
} ?: println("not a single file had a line eq to '123'")
}
But that doesn't stop on the match (when block() returns non-null) and goes to consume all files and all their lines.
A simple for loop is enough to implement mapFirst:
fun <F,T> Sequence<F>.mapFirst(block: (F) -> T?): T? {
for (e in this) {
block(e)?.let { return it }
}
return null
}
If you need a solution without introducing your own extensions (though there's nothing wrong with it), you can use mapNotNull + firstOrNull combination:
files.asSequence()
.mapNotNull { /* read the first line and return not null if it's ok */ }
.firstOrNull()
I would not map the values you discard then, instead do it like this:
sequenceOf(1, 2, 3)
.firstOrNull() { it == 2 }
?.let { it * 2 } ?: 6
First you find the value that matches your condition, then you transform it too whatever you want. In case you don't find a matching element, you assign a default value (in this case 6).
Consider the following two classes:
class ObjectA(val objectBs: List<ObjectB>,
val otherFields: Any)
class ObjectB(val key: String,
val otherFields: Any)
The task is to find and return the first ObjectB with a certain key in a List of ObjectA.
Just achieving the goal is simple enough, but doing it nicely and efficiently seems rather tricky. I can't find anything like a "firstIn" or "findIn" function that would allow me to return another type than ObjectA when iterating on a list of ObjectA.
I have a few approaches, one of which looks pretty nice, but is very inefficient:
listOfA.mapNotNull {
it.objectBs.firstOrNull {
item -> item.key == wantedKey
}
}.firstOrNull()
The obvious inefficiency of this code is that it will not stop iterating through listOfA when it has found a match (and there can only be one match, just to be clear).
Approaches using filter or find have similar problems, requiring redundant iterations through at least one list of ObjectB.
Is there something in kotlins standard library that would cover such a use case?
If you want an elegant solution you can just do a flatMap like this:
val result: ObjectB? = listOfA.flatMap { it.objectBs }.firstOrNull { it.key == "myKey" }
If you want the efficiency you can do something like this:
val result: ObjectB? = objectAs.firstOrNull {
it.objectBs.map(ObjectB::key).contains("myKey")
}?.objectBs?.firstOrNull { it.key == "myKey" }
You can also wrap these in an Optional and put it in a function so the users of this operation can have a clean API:
fun List<ObjectA>.findFirstObjectB(key: String): Optional<ObjectB> {
return Optional.ofNullable(firstOrNull {
it.objectBs.map(ObjectB::key).contains(key)
}?.objectBs?.firstOrNull { it.key == key })
}
By converting all the nested elements to a flattened Sequence, they can be iterated lazily, and the overhead of unnecessary iteration is eliminated. This trick is done by combining asSequence and flatMap:
listOfA.asSequence().flatMap { it.objectBs.asSequence() }.find { it.key == wantedKey }
I wrote and ran the following code to ensure that it works as expected:
class PrintSequenceDelegate<out T>(private val wrappedSequence: Sequence<T>) : Sequence<T> by wrappedSequence {
override fun iterator(): Iterator<T> {
val wrappedIterator = wrappedSequence.iterator()
return object : Iterator<T> by wrappedIterator {
override fun next(): T =
wrappedIterator.next().also { println("Retrieving: $it") }
}
}
}
fun <T> Sequence<T>.toPrintDelegate() = PrintSequenceDelegate(this)
fun main() {
val listOfLists = List(3) { i -> List(3) { j -> "$i$j" } }
println("List of lists: $listOfLists")
val found = listOfLists.asSequence().toPrintDelegate().flatMap { it.asSequence().toPrintDelegate() }.find { it == "11" }
println(if (found != null) "Found: $found" else "Not found")
}
Output:
List of lists: [[00, 01, 02], [10, 11, 12], [20, 21, 22]]
Retrieving: [00, 01, 02]
Retrieving: 00
Retrieving: 01
Retrieving: 02
Retrieving: [10, 11, 12]
Retrieving: 10
Retrieving: 11
Found: 11
Thus we see that the elements (12) after the element found in the containing nested list are not iterated, neither are the following nested lists ([20, 21, 22]).
Nothing fancy, but it does the job efficiently:
fun findBWithKey(listOfA: List<ObjectA>, wantedKey: String): ObjectB? {
listOfA.forEach {
it.objectBs.forEach { item ->
if(item.key == wantedKey){
return item
}
}
}
return null
}
I also like to use map and first, but doing the given task efficiently gets unecessary hard using those extension functions.
A simple flatMap does the trick:
listOfA.flatMap { it.objectBs }.first { it.key == wantedKey }
This will basically give you an intermediate List with all of them combined so that you can easily query the first matching one.
I would look in to coroutines or sequences if performance is critical.
You can optimize your code slightly by using firstOrNull on listOfA as well:
listOfA.filterNotNull().firstOrNull { item ->
item.objectBs.firstOrNull { it.key == wantedKey } != null
}
I would do some performance testing to see if this code is causing any issues before making it overly complex.
I need to map a list and retrieve the first non null element, and I need the map operation to be short circuited like it should be in Java 8 streams API. Is there a ready way to do this in Kotlin, without Java 8 streams?
I created my own extension method to do this:
fun <T, R> Iterable<T>.firstNonNullMapping(transform: (T) -> R?): R? {
for (element in this) {
val result = transform(element)
if (result != null) {
return result
}
}
return null
}
A test proves that this works
val firstNonNullMapping = listOf(null, 'a', 'b')
.firstNonNullMapping {
assertNotEquals(it, 'b') // Mapping should be stopped before reaching 'b'
it
}
assertEquals(firstNonNullMapping, 'a')
IntelliJ, however, suggest that I replace my for loop with the much neater
return this
.map { transform(it) }
.firstOrNull { it != null }
Problem is that this will map all elements of the iterable, and it is essential to my use case that is stops at the first non null element.
Kotlin has lazily evaluated sequences that correspond to Java 8 streams, instead of invoking stream() on a collection, you invoke asSequence():
return this
.asSequence()
.mapNotNull { transform(it) }
.firstOrNull()
Kotlin 1.5 provides a shortcut replacement for the combination of
.asSequence(), .mapNotNull { ... } and .first/firstOrNull(): the functions firstNotNullOf and firstNotNullOfOrNull respectively.
They execute the transform function once for each element and stop as soon as they encounter the first non-null result of that function, so they are short-circuiting.
I wonder what is faster, the #ingoKegel's solution, or this:
return this.firstOrNull { transform(it) != null }?.let { transform(it) }
I am using sortedBy() to perform sorting on the collection of objects.
Since the order may change depending on the user choice, I've ended up with the following code
val sortedList = if (sortingOrder == WordSortingOrder.BY_ALPHA) {
list.sortedBy { it.word.value }
} else {
list.sortedBy { it.createdAt }
}
Then I perform further actions on the sorted collection.
I realize that sortedBy() method expects a property to be returned.
I wonder if there is a way to embed the sorting condition in one chain of collection methods.
If your properties are of different types you won't be able to select one of them based on some condition as a result for sortedBy, as their common supertype would be inferred as Any and it is not a subtype of Comparable<R> as sortedBy expects.
Instead you can utilize sortedWith method, which takes a Comparator, and provide a comparator depending on the condition:
list.sortedWith(
if (sortingOrder == WordSortingOrder.BY_ALPHA)
compareBy { it.word.value }
else
compareBy { it.createdAt }
)
Comparators for different properties are created here with the kotlin.comparisons.compareBy function.
You can then extract the logic which selects comparator based on sorting order to a function:
list.sortedWith(comparatorFor(sortingOrder))
fun comparatorFor(sortingOrder: WordSortingOrder): Comparator<MyType> = ...
The sortedBy expects any function of type (T) -> R as its parameter. A property is a corner case of that.
Which means you can do this:
val sortedList = list
.sortedBy { if (sortingOrder == WordSortingOrder.BY_ALPHA) it.word.value else it.createdAt}
Or, if you need something more OOP-ish:
enum class WordSortingOrder(val transform: (MyObject) -> Int) {
BY_ALPHA({it.word.value}),
BY_ALPHA_REVERSED({-1 * it.word.value}),
DEFAULT({it.createdAt})
}
val sortedList = list.sortedBy { sortingOrder.transform(it)}
You can do something like:
list.sortedBy { item ->
when(sortingOrder) {
WordSortingOrder.BY_ALPHA -> item.word.value
else -> item.createdAt
}
}
You can make the lambda argument passed to sortedBy conditional:
list.sortedBy(if (sortingOrder == WordSortingOrder.BY_ALPHA) {
{ it: MyType -> it.word.value }
} else {
{ it: MyType -> it.createdAt }
})
You may find using when instead of if more readable in this scenario:
list.sortedBy(when (sortingOrder) {
WordSortingOrder.BY_ALPHA -> { it: MyType -> it.word.value }
else -> { it: MyType -> it.createdAt }
})
If your selectors have different return types then you can simply wrap your existing code within list.let { list -> ... } or use run:
list.run {
if (sortingOrder == WordSortingOrder.BY_ALPHA) {
sortedBy { it.word.value }
} else {
sortedBy { it.createdAt }
}
}
You can then continue chainging calls after the let/run.