The `kotlin.collections` package offers many functions to interact with collections.
In particular, functions `find(predicate)`, `findLast(predicate)`, `firstOrNull(predicate)` and "`lastOrNull(predicate)` return the element that matches the given predicate.

== Why is this an issue?

The functions `find(predicate)`, `findLast(predicate)`, `firstOrNull(predicate)` and "`lastOrNull(predicate)`
can be improperly used to check the presence of an element that matches the given predicate.
In such cases the code is more difficult to read and understand than it would be with the functions `any(predicate)`, `none(predicate)` or `contains(element)`.

=== What is the potential impact?

The pattern of using `find(predicate)`, `findLast(predicate)`, `firstOrNull(predicate)` and "`lastOrNull(predicate)`  combined with a null check,
to check the presence of an element is not immediately clear to readers.
For example, the expression `list.find { it > 5 } != null` is more difficult to understand than `list.any { it > 5 }`.
The additional comparison operator increases the complexity of the expression and introduces confusion about the intent of the code.

== How to fix it

Replace the use of `find(predicate)`, `findLast(predicate)`, `firstOrNull(predicate)` and "`lastOrNull(predicate)` with `any(predicate)`, `none(predicate)` or `contains(element)`.

There are four possible scenarios:

1. The predicate is a simple binary expression checking for equality, and the found object is compared to not be `null`:
    - Replace `find { it == element } != null` with `contains(element)`.
    - Replace `findLast { it == element } != null` with `contains(element)`.
    - Replace `firstOrNull { x -> x == element } != null` with `contains(element)`.
    - Replace `lastOrNull { x -> x == element } != null` with `contains(element)`.

2. The predicate is a simple binary expression checking for equality, and the found object is compared to be `null`:
    - Replace `find { it == element } == null` with `!contains(element)`.
    - Replace `findLast { it == element } == null` with `!contains(element)`.
    - Replace `firstOrNull  { x -> x == element } == null` with `!contains(element)`.
    - Replace `lastOrNull  { x -> x == element } == null` with `!contains(element)`.

3. The predicate is any binary expression not checking for equality, and the found object is compared to not be `null`:
    - Replace `find { it > 5 } != null` with `any { it > 5 }`.
    - Replace `findLast { it != 5 } != null` with `any { it != 5 }`.
    - Replace `firstOrNull { x -> x < 5 } != null` with `any { x -> x < 5 }`.
    - Replace `lastOrNull { x -> x != 5 } != null` with `any { x -> x != 5 }`.

4. The predicate is any binary expression not checking for equality, and the found object is compared to be `null`:
    - Replace `find { it > 5 } == null` with `none { it > 5 }`.
    - Replace `findLast { it != 5 } == null` with `none { it != 5 }`.
    - Replace `firstOrNull { x -> x < 5 } == null` with `none { x -> x < 5 }`.
    - Replace `lastOrNull { x -> x != 5 } == null` with `none { x -> x != 5 }`.

=== Code examples

==== Noncompliant code example

[source,kotlin,diff-id=1,diff-type=noncompliant]
----
fun example(list: List<Int>) {
    list.find { it > 5 } != null // Noncompliant
    list.findLast { it > 5 } == null // Noncompliant
    list.firstOrNull { it == 5 } != null // Noncompliant
    list.lastOrNull { x -> x == 5 } != null // Noncompliant
    list.find { x -> 5 == 4 } != null // Noncompliant, note that this case cannot be fixed using contains
}
----

==== Compliant solution

[source,kotlin,diff-id=1,diff-type=compliant]
----
fun example(list: List<Int>) {
    list.any { it > 5 } // Compliant
    list.none { it > 5 } // Compliant
    list.contains(5) // Compliant
    !list.contains(5) // Compliant
    list.any { x -> 5 == 4 } // Compliant, note that this case cannot be fixed using contains
}
----

== Resources

=== Documentation

* https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/[Kotlin API Docs, Package kotlin.collections]