49

In Kotlin we can do:

val arr = intArrayOf(1,2,3)
if (2 in arr)
   println("in list")

But if I want to check if 2 or 3 are in arr, what is the most idiomatic way to do it other than:

if (2 in arr || 3 in arr)
   println("in list")
ADev
  • 5,259
  • 2
  • 16
  • 29
  • See also https://stackoverflow.com/questions/8708542/something-like-contains-any-for-java-set for effective approach with large sets on JVM. – Vadzim Oct 17 '22 at 12:40

11 Answers11

59

I'd use any() extension method:

arrayOf(1, 2, 3).any { it == 2 || it == 3 }

This way, you traverse the array only once and you don't create a set instance just to check whether it's empty or not (like in one of other answers to this question).

aga
  • 27,954
  • 13
  • 86
  • 121
54

This is the shortest and most idiomatic way I can think of using any and in:

val values = setOf(2, 3)
val array = intArrayOf(1, 2, 3)

array.any { it in values }

Of course you can use a functional reference for the in operator as well:

array.any(values::contains)

I use setOf for the first collection because order does not matter.

Edit: I switched values and array, because of alex.dorokhow's answer. The order doesn't matter for the check to work but for performance.


The OP wanted the most idiomatic way of solving this. If you are after a more efficient way, go for aga's answer.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
25

You can use intersect method, it takes iterable as paramter and returns set containing only items that are both in your collection and in iterable you provided. Then on that set you just need to do size check.

Here is sample:

val array1 = arrayOf(1, 2, 3, 4, 5, 6)
val array2 = arrayOf(2, 5)

// true if array1 contains any of the items from array2
if(array1.intersect(array2.asIterable()).isNotEmpty()) {
    println("in list")
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Mateusz
  • 681
  • 5
  • 9
12

Combining @aga and @willi-mentzel solutions for better efficiency and dynamic set of checked values:

val numbers = setOf(2, 3)
arrayOf(1, 2, 3).any(numbers::contains)

Is this case the array will be iterated completely only once (at most, in the worst case).

This is more efficient than (suggested by @WilliMentzel):

numbers.any(arrayOf(1, 2, 3)::contains) // don't do that!

Where the array will be iterated set.count times in the worst case.

Note that Set.contains has O(1) complexity, but IntArray::contains has O(N).

Of course, this optimization makes sense only if the set or array are big enough.

alex.dorokhov
  • 1,218
  • 12
  • 17
  • Why do you say Set.contains is O(1) complexity and IntArray.contains is O(N)? Both will check each index if it contains the searched value and will therefore be O(N) complexity no? – alexbchr May 11 '18 at 17:44
  • @alexbchr, I ment that checking for a single element will be O(1) and O(N). For the complete code it is multiplied by N, so it becomes O(N) and O(N^2) for the first and second snippet respectively. The implementation of Set is usually optimized for a fast lookup. – alex.dorokhov May 13 '18 at 16:53
5

Another handy way is actually not Kotlin's but with use of Java Collections.
It is also good to know it.

Collections.disjoint(Collection<?> c1, Collection<?> c2)

Returns {@code true} if the two specified collections have no elements in common.

@Test
fun disjointCollections() {
    val list = listOf(1, 2, 3)

    assertTrue(Collections.disjoint(list, listOf(7, 8)))
    assertFalse(Collections.disjoint(list, listOf(1)))
}
Leo DroidCoder
  • 14,527
  • 4
  • 62
  • 54
4

For me, the best way to solve this is to define a containsAny(elements:) extension function on the Array and/or Collection class. All of the places where you need to check whether an Array or Collection contains any of the elements of another then remains nice and neat, as follows:

arrayOf(1, 2, 3).containsAny(2, 3)

The implementation detail is tucked away inside of that function and can be changed in a single place in future should you wish to change the implementation.

Here's an example implementation of an extension function defined on the Collection class, the details of which are inspired by other answers in this thread:

/**
 * Returns true if the receiving collection contains any of the specified elements.
 *
 * @param elements the elements to look for in the receiving collection.
 * @return true if any element in [elements] is found in the receiving collection.
 */
fun <T> Collection<T>.containsAny(vararg elements: T): Boolean {
    return containsAny(elements.toSet())
}

/**
 * Returns true if the receiving collection contains any of the elements in the specified collection.
 *
 * @param elements the elements to look for in the receiving collection.
 * @return true if any element in [elements] is found in the receiving collection.
 */
fun <T> Collection<T>.containsAny(elements: Collection<T>): Boolean {
    val set = if (elements is Set) elements else elements.toSet()
    return any(set::contains)
}

Likewise, here's an extension function defined on the Array class:

/**
 * Returns true if the receiving array contains any of the specified elements.
 *
 * @param elements the elements to look for in the receiving array.
 * @return true if any element in [elements] is found in the receiving array.
 */
fun <T> Array<T>.containsAny(vararg elements: T): Boolean {
    return any(elements.toSet()::contains)
}
Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
  • Your implementation is amazing but it can be reduced to one line: fun Collection.containsAny(vararg elements: T): Boolean = any(elements.toSet()::contains) – Barry Fruitman Jan 04 '23 at 18:18
2

I think it's most readable to write the statement the other way around:

val arr = intArrayOf(1,2,3)
val match = setOf(2, 3).any(arr::contains)

It might be even possible to use ranges in certain scenarios:

val match = (2..3).any(arr::contains)

In the end, your solution looks pretty good to me already. Although not using fancy library functionality.

s1m0nw1
  • 76,759
  • 17
  • 167
  • 196
  • Even though it wasn't relevant to the question, you taught me about `(1..2)` instead of `IntRange(1, 2)`. Thanks. – byxor Jan 04 '18 at 16:52
  • Note: It should be `2..3`, not `1..2`. – byxor Jan 04 '18 at 16:55
  • 1
    Arent you traversing the array multiple times? It isnt optimal. – Mateusz Jan 04 '18 at 17:04
  • 1
    If you are after performance I wouldnt suggest doing this, because this is traversing `arr` array multiple times. I would suggest using agas answer or mine, depending on amount of numbers to check. – Mateusz Jan 04 '18 at 17:13
  • You should come up with a comparison then. How many values will actually make this answer bad – s1m0nw1 Jan 04 '18 at 17:16
  • @s1m0nw1 your method has complexity `O(arr1.lenght * arr2.lenght)` while using intersect has complexity of `O(n)` because it uses `retainAll`([source](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/MutableCollections.kt#L240)) method on HashSet ([source](https://stackoverflow.com/questions/24754881/what-is-the-time-and-space-complexity-of-method-retainall-when-used-on-hashsets)). However number would have to be quite high for this to make noticeable difference – Mateusz Jan 04 '18 at 17:41
2

You can make an extension function to check for more values:

infix fun <T> Iterable<T>.containsAny(values: Iterable<T>): Boolean =
    intersect(values).isNotEmpty()
Francis
  • 6,788
  • 5
  • 47
  • 64
0

Have a look at this blog post: https://www.codevscolor.com/kotlin-check-array-contains-one-multiple-values

Sample code

fun main() {
    val givenArray = intArrayOf(1, 2, 3, 4, 5, 7, 9, 11)

    println(givenArray.any{ it == 5 || it == 12})
    println(givenArray.any{ it == 5 || it == 11})
    println(givenArray.any{ it == 15 || it == 21})
}
Thiago
  • 12,778
  • 14
  • 93
  • 110
0

If you use Apache Commons in your project, you can use CollectionUtils.containsAny.

u8188526
  • 171
  • 2
  • 5
0

we can use any() funtion and a Set

val arr = intArrayOf(1,2,3)

if(arr.any{ it in setOf(2,3) })
   println("in list")