Kotlin: Filtering a list return the original list if no match found - kotlin

I want an inline function that filters through a list and returns the original list if there is no match rather than returning null
I have this feature that filters through the list and returns the filtered list or empty
fun List<Obj>?.filterQueryText(queryText : String?) = this?.filter {
queryText.equals(it.objName)
}.orEmpty()
How do I make it so that it will return to the original list if no match is found in the query?

This should do the trick
fun List<Obj>?.filterQueryText(queryText : String?) = this?.filter {
queryText.equals(it.objName)
}?.run {if (isEmpty()) this#filterQueryText else this}.orEmpty()
see it in action here

Generic filter for List<T>:
fun <T> List<T>?.filterOrOriginal(predicate: (T) -> Boolean) : List<T>? =
this?.filter(predicate).takeUnless { it.isNullOrEmpty() } ?: this
Specific filter for List<Obj>:
fun List<Obj>?.filterQueryTextOrOriginal(queryText : String?) : List<Obj>? =
this?.filterOrOriginal { queryText == it.objName }

Related

Is there a Kotlin equivalent for Groovy's findIndexValues?

Does Kotlin have something to filter a collection and return the matching indexes?
E.g. like Groovy's findIndexValues:
http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Iterable.html#findIndexValues(groovy.lang.Closure)
Something like:
fun <T> List<T>.findIndexValues(predicate: (T) -> Boolean): List<Int> {
var indexValues = mutableListOf<Int>()
this.forEachIndexed { index, it ->
if (predicate(it)) {
indexValues.add(index)
}
}
return indexValues
}
The simplest way I can think of to do this is to use mapIndexedNotNull:
fun <T> List<T>.findIndexValues(predicate: (T) -> Boolean): List<Int> =
mapIndexedNotNull { i, t -> i.takeIf { predicate(t) } }
I don't believe there's a function for this in the standard library.
There are basically 2 simple ways according to me.
//say there is a List x of Strings
val x = listOf<String>()
//I don't believe you are looking for this.
//string is the required String of x at index.
for ((index, string) in x.withIndex()) {
TODO()
}
//2nd method is using filterIndexed
/**
* Returns a list containing only elements matching the given predicate.
* #Params: predicate - function that takes the index of an element and the element itself and returns the result of predicate evaluation on the element.
*/
x.filterIndexed { index, string ->
TODO()
}
I like #Sam's answer, but I find this implementation to be slightly more readable as it filters explicitly on predicate as opposed to implicitly via null:
fun <T> List<T>.findIndexValues(predicate: (T) -> Boolean): List<Int> =
withIndex().filter { (_, t) -> predicate(t) }.map { it.index }

How to find last node that satisfies where predicate in singly linked list?

write a method "lastWhere" that accepts a function called "where" of type (T) -> Boolean. The method returns the last element of type T to which the "where" function applies. If no matching element is found, null is returned.
call the method "lastwhere" on the linked list below. Find the last game that is more than 10 euros.
So far I've got this Code going for me.
I assume the only important piece of Code I need to edit is the "fun lastWhere" for task number 1)
the second task wants me to implement a way on the main function to find the last Game that is cheaper than 10 Euros.
class LinkedList<T> {
data class Node<T>(val data: T, var next: Node<T>?)
private var first: Node<T>? = null
override fun toString(): String = first?.toString() ?: "-"
fun isEmpty() = first == null
fun addLast(data: T) {
if (first == null) {
first = Node(data, first)
return
}
var runPointer = first
while (runPointer?.next != null) {
runPointer = runPointer.next
}
runPointer?.next = Node(data, null)
}
fun lastWhere (where: (T) -> Boolean): T? { // "where" function needs to be implemented
if (isEmpty()) return null
else {
var runPointer = first
while (runPointer?.next != null ) {
runPointer = runPointer.next
}
return runPointer?.data
}
}
}
data class Game(val title: String, val price: Double)
fun main() {
val list = LinkedList<Game>()
list.addLast(Game("Minecraft", 9.99))
list.addLast(Game("Overwatch", 29.99))
list.addLast(Game("Mario Kart", 59.99))
list.addLast(Game("World of Warcraft", 19.99))
var test = list.lastWhere ({it.price >= 10.00}) // This is probably wrong too, since I haven't got task 1) working
println (test)
}
Would appreciate any help!
Since you only store a reference to first node, you don't have any choice but to start at first and iterate. you will also have to keep a reference to last item that satisfied the where predicate, and keep updating this reference with every iteration.
fun lastWhere (where: (T) -> Boolean): T? {
var runPointer = first
var item: T? = null // init item to null, if nothing is found we return null
while (runPointer != null ) {
// For every node, execute the where function and if it returns true
// then update the return value
if(where(runPointer.data)) { item = runPointer.data }
runPointer = runPointer.next
}
return item
}

Receive transformation map function to send into `List<>.mapNotNull()`

Im trying to write a function like transform that receives a function that will be used inside of mapNotNull but I cant find a way to do it.
Example
val items: List<String?> = listOf(null, "cosa")
fun transform(transformer: (String) -> String?) {
items.mapNotNull(transformer) // <-------------------------------------- THIS DOES NOT COMPILE
}
fun main() {
val items: List<String?> = listOf(null, "cosa")
val transformer: (String) -> String? = {
null
}
val map = transform(transformer)
print(map)
}
You can check how this works here: play.kotlinlang
How can I declare the parameter of fun transform to be able to pass it inside of the mapNotNull ?
The mapNotNull function is defined as:
public inline fun <T, R : Any> Iterable<T>.mapNotNull(transform: (T) -> R?): List<R>
in other words, the type of the parameter to the transform lambda is T, where T is the type of the Iterable being operated on. In your case, your iterable is a List of type String?.
Therefore, you need to declare your transformer as type (String?) -> String?, and only the non-null results of that transform will be included in the result.
To update the code you supplied on play.kotlinlang, with a few additional modifications to make the type declarations a bit more idiomatic -- note, I've left the code mostly as-is, despite the odd use of the additional transform function:
val items = listOf<String?>(null, "cosa")
fun transform (transformer: (String?) -> String?): List<String> {
return items.mapNotNull(transformer)
}
fun main() {
val items = listOf<String?>(null, "cosa")
val transformer: (String?) -> String? = {
// this of course means the output of transform will always be empty
null
}
val map = transform(transformer)
print(map)
}
You have a list of nullable strings.
mapNotNull applies the transform function to an each element in a list and then checks if the result of this functions is null. So in this case, it passes a nullable string in the transformer function, and that function definitely cannot be of (String) -> String? type because the parameter here is a non-nullable string.
You should either declare the transformer function as (String?) -> String?, or remove nulls from list before calling mapNotNull:
items.filterNotNull().mapNotNull(transformer)
Another option is to wrap transformer into a lambda function before passing it to mapNotNull and handle null elements there, for example:
items.mapNotNull { e -> e?.let(transformer) }
this applies transformer function to an element only if it is not null.

Traverse a list of filter to appy them

I have some filter on a list(10..1000).
I would like to have a list of function objects, traverse this list and apply that filter to (10..1000).
Maybe later choose some filters.
fun main(args: Array<String>) {
var sol = (10..1000).toList().filter(lastDigitIsLength ).filter(no7andNo1 ).filter(isEvenAndGreater1).filter(first2DigitsOddCrossSumLess10 ).filter(isPrime )
println("The number is $sol")
}
/* The functions exist
There are few ways to do that.
I've listed them in my example, along with some general comments.
// No need for varargs since Kotlin 1.3
fun main() {
// Prefer vals over vars
val sol = (10..1000).
asSequence().// Use asSequence instead of toList
filter { lastDigitIsLength(it) }. // Block
filter(::no7andNo1). // Method reference
toList()
println("The number is $sol")
}
// Don't know what it means, so lets just always return true
fun lastDigitIsLength(i: Int) = true // Short notation
fun no7andNo1(i: Int): Boolean {
return i % 10 == 7 || i % 10 == 1
}
Sorry, maybe I misunderstand your question. Do you want to apply a list of filters to list of ints? If so, it can be done like this:
fun filter(list: List<Int>, filters: List<(Int) -> Boolean>): List<Int> {
var result = list
for (filter in filters) {
result = result.filter(filter)
}
return result
}
so you can rewrite your function as:
fun main() {
val sol = filter((10..1000).toList(), listOf(::lastDigitIsLength, ::no7andNo1, ::isEvenAndGreater1))
println("The number is $sol")
}

Idiomatic way to create a variable spring data mongodb Query in Kotlin?

Using Kotlin and spring data mongodb, I'm trying to find the most idiomatic method that can receive nullable parameters (through user defined filters) and create a MongoTemplate Query with the non-null ones (which, in Java, would be a bunch of ifs). This is what I came up with, so far, but I wonder if there's a better way:
// extending Query
open class Filter {
// case insensitive 'like' criteria
fun Query.like(field: String, value: String?) = value?.let {
this.addCriteria(Criteria.where(field).regex(it,"i"))
}
// queries from a date, to a date or between two dates
fun <T: Comparable<T>> Query.between(field: String, from: T?, to: T?) {
if (from != null || to != null) {
val criteria = where(field)
from?.let { criteria.gte(it) }
to?.let { criteria.lte(it) }
this.addCriteria(criteria)
}
}
fun Query.onlyActive(active: Boolean?) = when (active) {
true -> this.addCriteria(Criteria.where("active").`is`(true))
else -> null
}
}
// data class extending Filter()
data class myFilter(val: name: String, val type: String?, val content:String?,
val fromNum: Int?, val toNum: Int?, val fromDate: LocalDateTime?,
val toDate: LocalDateTime?, val active? = false): Filter() {
fun toQuery(): Query {
// name is a mandatory param
val query = Query.query(Criteria.where("name").`is`(name))
with(query) {
like("type", type)
like("content", content)
between("num", fromNum, toNum)
between("date", fromDate, toDate)
onlyActive(active)
}
return query
}
}