diff --git a/libraries/stdlib/common/src/generated/_Arrays.kt b/libraries/stdlib/common/src/generated/_Arrays.kt index dcc7a33f51adf..f8099287824f1 100644 --- a/libraries/stdlib/common/src/generated/_Arrays.kt +++ b/libraries/stdlib/common/src/generated/_Arrays.kt @@ -1031,6 +1031,16 @@ public inline fun CharArray.find(predicate: (Char) -> Boolean): Char? { return firstOrNull(predicate) } +/** + * Returns the first transformed element matching specified type parameter R, or `null` if no such element was found. + * + * @sample samples.collections.Collections.Elements.findIs + */ +@kotlin.internal.InlineOnly +public inline fun Array<*>.findIs(): R? { + return find { element -> element is R } as R? +} + /** * Returns the last element matching the given [predicate], or `null` if no such element was found. * @@ -1301,6 +1311,16 @@ public inline fun CharArray.first(predicate: (Char) -> Boolean): Char { throw NoSuchElementException("Array contains no element matching the predicate.") } +/** + * Returns the first element matching the specified type R. + * @throws [NoSuchElementException] if no such element of specified type R is found. + */ +@kotlin.internal.InlineOnly +public inline fun Array<*>.firstIs(): R? { + for (element in this) if (element is R) return element + throw NoSuchElementException("Array contains no element matching the specified type ${R::class.simpleName}.") +} + /** * Returns the first non-null value produced by [transform] function being applied to elements of this array in iteration order, * or throws [NoSuchElementException] if no non-null value was produced. diff --git a/libraries/stdlib/common/src/generated/_Collections.kt b/libraries/stdlib/common/src/generated/_Collections.kt index 28b734cbd57f1..1238b340339a3 100644 --- a/libraries/stdlib/common/src/generated/_Collections.kt +++ b/libraries/stdlib/common/src/generated/_Collections.kt @@ -175,6 +175,16 @@ public inline fun Iterable.find(predicate: (T) -> Boolean): T? { return firstOrNull(predicate) } +/** + * Returns the first transformed element matching specified type parameter R, or `null` if no such element was found. + * + * @sample samples.collections.Collections.Elements.findIs + */ +@kotlin.internal.InlineOnly +public inline fun Iterable<*>.findIs(): R? { + return find { element -> element is R } as R? +} + /** * Returns the last element matching the given [predicate], or `null` if no such element was found. * @@ -232,6 +242,16 @@ public inline fun Iterable.first(predicate: (T) -> Boolean): T { throw NoSuchElementException("Collection contains no element matching the predicate.") } +/** + * Returns the first element matching the specified type R. + * @throws [NoSuchElementException] if no such element of specified type R is found. + */ +@kotlin.internal.InlineOnly +public inline fun Iterable<*>.firstIs(): R? { + for (element in this) if (element is R) return element + throw NoSuchElementException("Collection contains no element matching the specified type ${R::class.simpleName}.") +} + /** * Returns the first non-null value produced by [transform] function being applied to elements of this collection in iteration order, * or throws [NoSuchElementException] if no non-null value was produced. diff --git a/libraries/stdlib/common/src/generated/_Sequences.kt b/libraries/stdlib/common/src/generated/_Sequences.kt index e54ba3b09c412..f45bfbc992f1c 100644 --- a/libraries/stdlib/common/src/generated/_Sequences.kt +++ b/libraries/stdlib/common/src/generated/_Sequences.kt @@ -92,6 +92,18 @@ public inline fun Sequence.find(predicate: (T) -> Boolean): T? { return firstOrNull(predicate) } +/** + * Returns the first transformed element matching specified type parameter R, or `null` if no such element was found. + * + * The operation is _terminal_. + * + * @sample samples.collections.Collections.Elements.findIs + */ +@kotlin.internal.InlineOnly +public inline fun Sequence<*>.findIs(): R? { + return find { element -> element is R } as R? +} + /** * Returns the last element matching the given [predicate], or `null` if no such element was found. * @@ -129,6 +141,18 @@ public inline fun Sequence.first(predicate: (T) -> Boolean): T { throw NoSuchElementException("Sequence contains no element matching the predicate.") } +/** + * Returns the first element matching the specified type R. + * @throws [NoSuchElementException] if no such element of specified type R is found. + * + * The operation is _terminal_. + */ +@kotlin.internal.InlineOnly +public inline fun Sequence<*>.firstIs(): R? { + for (element in this) if (element is R) return element + throw NoSuchElementException("Sequence contains no element matching the specified type ${R::class.simpleName}.") +} + /** * Returns the first non-null value produced by [transform] function being applied to elements of this sequence in iteration order, * or throws [NoSuchElementException] if no non-null value was produced. diff --git a/libraries/stdlib/samples/test/samples/collections/collections.kt b/libraries/stdlib/samples/test/samples/collections/collections.kt index 05b2f823d1ab0..9a49678b961de 100644 --- a/libraries/stdlib/samples/test/samples/collections/collections.kt +++ b/libraries/stdlib/samples/test/samples/collections/collections.kt @@ -1233,6 +1233,43 @@ class Collections { assertPrints(lastEven, "6") } + @Sample + fun findIs() { + val objects = listOf("one", 1, true, null) + val string = objects.findIs() + val boolean = objects.findIs() + val number = objects.findIs() + val int = objects.findIs() + val long = objects.findIs() + + assertPrints(string, "one") + assertEquals(number, 1) + assertEquals(int, 1) + assertTrue { boolean == true } + assertNull(long) + } + + @Sample + fun firstIs() { + val objects = listOf("one", 1, true, null) + val string = objects.firstIs() + val boolean = objects.firstIs() + val number = objects.firstIs() + val int = objects.firstIs() + + assertPrints(string, "one") + assertEquals(number, 1) + assertEquals(int, 1) + assertTrue { boolean == true } + + val exception = assertFails { + objects.firstIs() + } + + assertIs(exception) + assertEquals("Collection contains no element matching the specified type Long.", exception.message) + } + @Sample fun getOrElse() { val list = listOf(1, 2, 3) diff --git a/libraries/tools/binary-compatibility-validator/klib-public-api/kotlin-stdlib.api b/libraries/tools/binary-compatibility-validator/klib-public-api/kotlin-stdlib.api index 4277d9e4689a6..dc8ce9fb80193 100644 --- a/libraries/tools/binary-compatibility-validator/klib-public-api/kotlin-stdlib.api +++ b/libraries/tools/binary-compatibility-validator/klib-public-api/kotlin-stdlib.api @@ -13876,6 +13876,24 @@ final inline fun <#A: kotlin/Any?> (kotlin.collections/Collection<#A>).kotlin.co // Targets: [js, wasmJs, wasmWasi] final inline fun <#A: kotlin/Any?> kotlin/emptyArray(): kotlin/Array<#A> // kotlin/emptyArray|emptyArray(){0§}[0] +// Targets: [js, wasmJs, wasmWasi] +final inline fun <#A: reified kotlin/Any?> (kotlin.collections/Iterable<*>).kotlin.collections/findIs(): #A? // kotlin.collections/findIs|findIs@kotlin.collections.Iterable<*>(){0§}[0] + +// Targets: [js, wasmJs, wasmWasi] +final inline fun <#A: reified kotlin/Any?> (kotlin.collections/Iterable<*>).kotlin.collections/firstIs(): #A? // kotlin.collections/firstIs|firstIs@kotlin.collections.Iterable<*>(){0§}[0] + +// Targets: [js, wasmJs, wasmWasi] +final inline fun <#A: reified kotlin/Any?> (kotlin.sequences/Sequence<*>).kotlin.sequences/findIs(): #A? // kotlin.sequences/findIs|findIs@kotlin.sequences.Sequence<*>(){0§}[0] + +// Targets: [js, wasmJs, wasmWasi] +final inline fun <#A: reified kotlin/Any?> (kotlin.sequences/Sequence<*>).kotlin.sequences/firstIs(): #A? // kotlin.sequences/firstIs|firstIs@kotlin.sequences.Sequence<*>(){0§}[0] + +// Targets: [js, wasmJs, wasmWasi] +final inline fun <#A: reified kotlin/Any?> (kotlin/Array<*>).kotlin.collections/findIs(): #A? // kotlin.collections/findIs|findIs@kotlin.Array<*>(){0§}[0] + +// Targets: [js, wasmJs, wasmWasi] +final inline fun <#A: reified kotlin/Any?> (kotlin/Array<*>).kotlin.collections/firstIs(): #A? // kotlin.collections/firstIs|firstIs@kotlin.Array<*>(){0§}[0] + // Targets: [js, wasmJs] open annotation class kotlin.js/ExperimentalWasmJsInterop : kotlin/Annotation { // kotlin.js/ExperimentalWasmJsInterop|null[0] constructor () // kotlin.js/ExperimentalWasmJsInterop.|(){}[0] diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt index f2c759ff2bf17..503cc82c6c0be 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt @@ -616,6 +616,25 @@ object Elements : TemplateGroupBase() { } } + val f_firstIs = fn("firstIs()") { + include(Iterables, Sequences, ArraysOfObjects) + } builder { + inlineOnly() + genericStarProjection = true + typeParam("reified R") + returns("R?") + + doc { """Returns the first ${f.element} matching the specified type R. + @throws [NoSuchElementException] if no such ${f.element} of specified type R is found.""" } + + body { + """ + for (element in this) if (element is R) return element + throw NoSuchElementException("${f.doc.collection.capitalize()} contains no ${f.doc.element} matching the specified type ${"$"}{R::class.simpleName}.") + """ + } + } + val f_find = fn("find(predicate: (T) -> Boolean)") { includeDefault() include(CharSequences, ArraysOfUnsigned) @@ -627,6 +646,26 @@ object Elements : TemplateGroupBase() { body { "return firstOrNull(predicate)"} } + val f_findIs = fn("findIs()") { + include(Iterables, Sequences, ArraysOfObjects) + } builder { + inlineOnly() + genericStarProjection = true + typeParam("reified R") + returns("R?") + + sample("samples.collections.Collections.Elements.findIs") + + doc { + "Returns the first transformed ${f.element} matching specified type parameter R, or `null` if no such ${f.element} was found." + } + body { + """ + return find { element -> element is R } as R? + """ + } + } + private val Family.sampleClass: String get() = when (this) { Strings, CharSequences -> "samples.text.Strings"