From bdd53ee9cdaffcafda5e88b6cce98bce088405c3 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Fri, 24 Apr 2020 05:15:07 +0300 Subject: [PATCH] Introduce new overloads of flatMap and flatMapTo - Sequence.flatMap((T) -> Iterable) - Iterable.flatMap((T) -> Sequence) - Array.flatMap((T) -> Sequence) - Map.flatMap((Entry) -> Sequence) KT-34506 --- .../stdlib/common/src/generated/_Arrays.kt | 28 +++++++++ .../common/src/generated/_Collections.kt | 28 +++++++++ .../stdlib/common/src/generated/_Maps.kt | 28 +++++++++ .../stdlib/common/src/generated/_Sequences.kt | 32 ++++++++++ .../stdlib/test/collections/ArraysTest.kt | 10 +++ .../stdlib/test/collections/CollectionTest.kt | 11 ++++ libraries/stdlib/test/collections/MapTest.kt | 17 ++++- .../stdlib/test/collections/SequenceTest.kt | 18 ++++-- .../kotlin-stdlib-runtime-merged.txt | 8 +++ .../src/templates/Mapping.kt | 62 ++++++++++++++++++- 10 files changed, 233 insertions(+), 9 deletions(-) diff --git a/libraries/stdlib/common/src/generated/_Arrays.kt b/libraries/stdlib/common/src/generated/_Arrays.kt index 990298e352a..d670e8686c9 100644 --- a/libraries/stdlib/common/src/generated/_Arrays.kt +++ b/libraries/stdlib/common/src/generated/_Arrays.kt @@ -10032,6 +10032,19 @@ public inline fun CharArray.flatMap(transform: (Char) -> Iterable): List< return flatMapTo(ArrayList(), transform) } +/** + * Returns a single list of all elements yielded from results of [transform] function being invoked on each element of original array. + * + * @sample samples.collections.Collections.Transformations.flatMap + */ +@SinceKotlin("1.4") +@OptIn(kotlin.experimental.ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +@kotlin.jvm.JvmName("flatMapSequence") +public inline fun Array.flatMap(transform: (T) -> Sequence): List { + return flatMapTo(ArrayList(), transform) +} + /** * Appends all elements yielded from results of [transform] function being invoked on each element of original array, to the given [destination]. */ @@ -10131,6 +10144,21 @@ public inline fun > CharArray.flatMapTo(destinati return destination } +/** + * Appends all elements yielded from results of [transform] function being invoked on each element of original array, to the given [destination]. + */ +@SinceKotlin("1.4") +@OptIn(kotlin.experimental.ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +@kotlin.jvm.JvmName("flatMapSequenceTo") +public inline fun > Array.flatMapTo(destination: C, transform: (T) -> Sequence): C { + for (element in this) { + val list = transform(element) + destination.addAll(list) + } + return destination +} + /** * Groups elements of the original array by the key returned by the given [keySelector] function * applied to each element and returns a map where each group key is associated with a list of corresponding elements. diff --git a/libraries/stdlib/common/src/generated/_Collections.kt b/libraries/stdlib/common/src/generated/_Collections.kt index c2498309394..af6ec87cda8 100644 --- a/libraries/stdlib/common/src/generated/_Collections.kt +++ b/libraries/stdlib/common/src/generated/_Collections.kt @@ -1286,6 +1286,19 @@ public inline fun Iterable.flatMap(transform: (T) -> Iterable): Lis return flatMapTo(ArrayList(), transform) } +/** + * Returns a single list of all elements yielded from results of [transform] function being invoked on each element of original collection. + * + * @sample samples.collections.Collections.Transformations.flatMap + */ +@SinceKotlin("1.4") +@OptIn(kotlin.experimental.ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +@kotlin.jvm.JvmName("flatMapSequence") +public inline fun Iterable.flatMap(transform: (T) -> Sequence): List { + return flatMapTo(ArrayList(), transform) +} + /** * Appends all elements yielded from results of [transform] function being invoked on each element of original collection, to the given [destination]. */ @@ -1297,6 +1310,21 @@ public inline fun > Iterable.flatMapTo(dest return destination } +/** + * Appends all elements yielded from results of [transform] function being invoked on each element of original collection, to the given [destination]. + */ +@SinceKotlin("1.4") +@OptIn(kotlin.experimental.ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +@kotlin.jvm.JvmName("flatMapSequenceTo") +public inline fun > Iterable.flatMapTo(destination: C, transform: (T) -> Sequence): C { + for (element in this) { + val list = transform(element) + destination.addAll(list) + } + return destination +} + /** * Groups elements of the original collection by the key returned by the given [keySelector] function * applied to each element and returns a map where each group key is associated with a list of corresponding elements. diff --git a/libraries/stdlib/common/src/generated/_Maps.kt b/libraries/stdlib/common/src/generated/_Maps.kt index b7158267a73..62b1e25bb81 100644 --- a/libraries/stdlib/common/src/generated/_Maps.kt +++ b/libraries/stdlib/common/src/generated/_Maps.kt @@ -46,6 +46,19 @@ public inline fun Map.flatMap(transform: (Map.Entry) - return flatMapTo(ArrayList(), transform) } +/** + * Returns a single list of all elements yielded from results of [transform] function being invoked on each entry of original map. + * + * @sample samples.collections.Collections.Transformations.flatMap + */ +@SinceKotlin("1.4") +@OptIn(kotlin.experimental.ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +@kotlin.jvm.JvmName("flatMapSequence") +public inline fun Map.flatMap(transform: (Map.Entry) -> Sequence): List { + return flatMapTo(ArrayList(), transform) +} + /** * Appends all elements yielded from results of [transform] function being invoked on each entry of original map, to the given [destination]. */ @@ -57,6 +70,21 @@ public inline fun > Map.flatMapTo return destination } +/** + * Appends all elements yielded from results of [transform] function being invoked on each entry of original map, to the given [destination]. + */ +@SinceKotlin("1.4") +@OptIn(kotlin.experimental.ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +@kotlin.jvm.JvmName("flatMapSequenceTo") +public inline fun > Map.flatMapTo(destination: C, transform: (Map.Entry) -> Sequence): C { + for (element in this) { + val list = transform(element) + destination.addAll(list) + } + return destination +} + /** * Returns a list containing the results of applying the given [transform] function * to each entry in the original map. diff --git a/libraries/stdlib/common/src/generated/_Sequences.kt b/libraries/stdlib/common/src/generated/_Sequences.kt index 5c627b1f13e..e90b8fbc159 100644 --- a/libraries/stdlib/common/src/generated/_Sequences.kt +++ b/libraries/stdlib/common/src/generated/_Sequences.kt @@ -765,6 +765,21 @@ public fun Sequence.toSet(): Set { return toCollection(LinkedHashSet()).optimizeReadOnlySet() } +/** + * Returns a single sequence of all elements from results of [transform] function being invoked on each element of original sequence. + * + * The operation is _intermediate_ and _stateless_. + * + * @sample samples.collections.Collections.Transformations.flatMap + */ +@SinceKotlin("1.4") +@OptIn(kotlin.experimental.ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +@kotlin.jvm.JvmName("flatMapIterable") +public fun Sequence.flatMap(transform: (T) -> Iterable): Sequence { + return FlatteningSequence(this, transform, { it.iterator() }) +} + /** * Returns a single sequence of all elements from results of [transform] function being invoked on each element of original sequence. * @@ -776,6 +791,23 @@ public fun Sequence.flatMap(transform: (T) -> Sequence): Sequence> Sequence.flatMapTo(destination: C, transform: (T) -> Iterable): C { + for (element in this) { + val list = transform(element) + destination.addAll(list) + } + return destination +} + /** * Appends all elements yielded from results of [transform] function being invoked on each element of original sequence, to the given [destination]. * diff --git a/libraries/stdlib/test/collections/ArraysTest.kt b/libraries/stdlib/test/collections/ArraysTest.kt index 8a51eb9d091..891b5889c90 100644 --- a/libraries/stdlib/test/collections/ArraysTest.kt +++ b/libraries/stdlib/test/collections/ArraysTest.kt @@ -12,6 +12,7 @@ import test.assertTypeEquals import test.collections.behaviors.* import test.comparisons.STRING_CASE_INSENSITIVE_ORDER import test.text.isAsciiLetter +import kotlin.math.exp import kotlin.test.* import kotlin.random.Random @@ -1756,6 +1757,15 @@ class ArraysTest { assertEquals(listOf(2), arrayOf("a", null, "test").mapIndexedNotNull { index, it -> it?.run { if (index != 0) length / index else null } }) } + @Test fun flatMap() { + val data = arrayOf(arrayOf(1, 2, 3), arrayOf(4, 5, 6)) + val result1 = data.flatMap { it.asList() } + val result2 = data.flatMap { it.asSequence() } + val expected = (data[0] + data[1]).toList() + assertEquals(expected, result1) + assertEquals(expected, result2) + } + @Test fun flattenArray() { val arr1: Array> = arrayOf(arrayOf(1, 2, 3), arrayOf(4, 5, 6)) val arr2: Array> = arr1 diff --git a/libraries/stdlib/test/collections/CollectionTest.kt b/libraries/stdlib/test/collections/CollectionTest.kt index 74cf9ff3ba5..f6af6c1447a 100644 --- a/libraries/stdlib/test/collections/CollectionTest.kt +++ b/libraries/stdlib/test/collections/CollectionTest.kt @@ -46,6 +46,17 @@ class CollectionTest { assertStaticAndRuntimeTypeIs>(foo) } + + @Test fun flatMap() { + val source = listOf(null, "foo", "bar") + val result1 = source.flatMap { it.orEmpty().asSequence() } + val result2 = source.flatMap { it.orEmpty().asIterable() } + + val expected = "foobar".toList() + assertEquals(expected, result1) + assertEquals(expected, result2) + } + /* @Test fun mapNotNull() { val data = listOf(null, "foo", null, "bar") diff --git a/libraries/stdlib/test/collections/MapTest.kt b/libraries/stdlib/test/collections/MapTest.kt index bbc62e9354d..7aa0347205e 100644 --- a/libraries/stdlib/test/collections/MapTest.kt +++ b/libraries/stdlib/test/collections/MapTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -216,6 +216,21 @@ class MapTest { assertEquals(mapOf(8 to "Mells"), m3) } + @Test fun flatMap() { + fun list(entry: Map.Entry): List = listOf(entry.key, entry.value) + fun seq(entry: Map.Entry): Sequence = sequenceOf(entry.key, entry.value) + val m = mapOf("x" to 1, "y" to 0) + val result1 = m.flatMap { list(it) } + val result2 = m.flatMap { seq(it) } + val result3 = m.flatMap(::list) + val result4 = m.flatMap(::seq) + val expected = listOf("x", 1, "y", 0) + assertEquals(expected, result1) + assertEquals(expected, result2) + assertEquals(expected, result3) + assertEquals(expected, result4) + } + @Test fun createFrom() { val pairs = arrayOf("a" to 1, "b" to 2) val expected = mapOf(*pairs) diff --git a/libraries/stdlib/test/collections/SequenceTest.kt b/libraries/stdlib/test/collections/SequenceTest.kt index 53e13ed9c78..5d159bf2ec1 100644 --- a/libraries/stdlib/test/collections/SequenceTest.kt +++ b/libraries/stdlib/test/collections/SequenceTest.kt @@ -572,18 +572,24 @@ public class SequenceTest { } @Test fun flatMap() { - val result = sequenceOf(1, 2).flatMap { (0..it).asSequence() } - assertEquals(listOf(0, 1, 0, 1, 2), result.toList()) + val result1 = sequenceOf(1, 2).flatMap { (0..it).asSequence() } + val result2 = sequenceOf(1, 2).flatMap { 0..it } + val expected = listOf(0, 1, 0, 1, 2) + assertEquals(expected, result1.toList()) + assertEquals(expected, result2.toList()) } @Test fun flatMapOnEmpty() { - val result = sequenceOf().flatMap { (0..it).asSequence() } - assertTrue(result.none()) + assertTrue(sequenceOf().flatMap { sequenceOf(1) }.none()) + assertTrue(sequenceOf().flatMap { listOf(1) }.none()) } @Test fun flatMapWithEmptyItems() { - val result = sequenceOf(1, 2, 4).flatMap { if (it == 2) sequenceOf() else (it - 1..it).asSequence() } - assertEquals(listOf(0, 1, 3, 4), result.toList()) + val result1 = sequenceOf(1, 2, 4).flatMap { if (it == 2) sequenceOf() else (it - 1..it).asSequence() } + val result2 = sequenceOf(1, 2, 4).flatMap { if (it == 2) emptyList() else it - 1..it } + val expected = listOf(0, 1, 3, 4) + assertEquals(expected, result1.toList()) + assertEquals(expected, result2.toList()) } @Test fun flatten() { diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 5fd0f25d182..73a1b27a334 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -1087,6 +1087,8 @@ public final class kotlin/collections/ArraysKt { public static final fun flatMap ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/util/List; public static final fun flatMap ([SLkotlin/jvm/functions/Function1;)Ljava/util/List; public static final fun flatMap ([ZLkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun flatMapSequence ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun flatMapSequenceTo ([Ljava/lang/Object;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun flatMapTo ([BLjava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun flatMapTo ([CLjava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun flatMapTo ([DLjava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; @@ -2136,6 +2138,8 @@ public final class kotlin/collections/CollectionsKt { public static final fun firstOrNull (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun firstOrNull (Ljava/util/List;)Ljava/lang/Object; public static final fun flatMap (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun flatMapSequence (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun flatMapSequenceTo (Ljava/lang/Iterable;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun flatMapTo (Ljava/lang/Iterable;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun flatten (Ljava/lang/Iterable;)Ljava/util/List; public static final fun fold (Ljava/lang/Iterable;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; @@ -2401,6 +2405,8 @@ public final class kotlin/collections/MapsKt { public static final fun filterTo (Ljava/util/Map;Ljava/util/Map;Lkotlin/jvm/functions/Function1;)Ljava/util/Map; public static final fun filterValues (Ljava/util/Map;Lkotlin/jvm/functions/Function1;)Ljava/util/Map; public static final fun flatMap (Ljava/util/Map;Lkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun flatMapSequence (Ljava/util/Map;Lkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun flatMapSequenceTo (Ljava/util/Map;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun flatMapTo (Ljava/util/Map;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun forEach (Ljava/util/Map;Lkotlin/jvm/functions/Function1;)V public static final fun getOrImplicitDefaultNullable (Ljava/util/Map;Ljava/lang/Object;)Ljava/lang/Object; @@ -4780,6 +4786,8 @@ public final class kotlin/sequences/SequencesKt { public static final fun firstOrNull (Lkotlin/sequences/Sequence;)Ljava/lang/Object; public static final fun firstOrNull (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun flatMap (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static final fun flatMapIterable (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static final fun flatMapIterableTo (Lkotlin/sequences/Sequence;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun flatMapTo (Lkotlin/sequences/Sequence;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; public static final fun flatten (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun flattenSequenceOfIterable (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Mapping.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Mapping.kt index 1e0bd205d25..010f5491678 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Mapping.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Mapping.kt @@ -304,7 +304,37 @@ object Mapping : TemplateGroupBase() { } specialFor(Sequences) { inline(Inline.No) - signature("flatMap(transform: (T) -> Sequence)") + since("1.4") + annotation("@OptIn(kotlin.experimental.ExperimentalTypeInference::class)") + annotation("@OverloadResolutionByLambdaReturnType") + annotation("""@kotlin.jvm.JvmName("flatMapIterable")""") + doc { "Returns a single sequence of all elements from results of [transform] function being invoked on each element of original sequence." } + returns("Sequence") + body { + "return FlatteningSequence(this, transform, { it.iterator() })" + } + } + } + + val f_flatMapSequence = fn("flatMap(transform: (T) -> Sequence)") { + include(Sequences, Iterables, ArraysOfObjects, Maps) + } builder { + inline() + doc { "Returns a single list of all elements yielded from results of [transform] function being invoked on each ${f.element} of original ${f.collection}." } + sample("samples.collections.Collections.Transformations.flatMap") + typeParam("R") + returns("List") + body { + "return flatMapTo(ArrayList(), transform)" + } + if (f != Sequences) { + since("1.4") + annotation("@OptIn(kotlin.experimental.ExperimentalTypeInference::class)") + annotation("@OverloadResolutionByLambdaReturnType") + annotation("""@kotlin.jvm.JvmName("flatMapSequence")""") + } + specialFor(Sequences) { + inline(Inline.No) doc { "Returns a single sequence of all elements from results of [transform] function being invoked on each element of original sequence." } returns("Sequence") body { @@ -322,7 +352,35 @@ object Mapping : TemplateGroupBase() { doc { "Appends all elements yielded from results of [transform] function being invoked on each ${f.element} of original ${f.collection}, to the given [destination]." } specialFor(Sequences) { - signature("flatMapTo(destination: C, transform: (T) -> Sequence)") + since("1.4") + annotation("@OptIn(kotlin.experimental.ExperimentalTypeInference::class)") + annotation("@OverloadResolutionByLambdaReturnType") + annotation("""@kotlin.jvm.JvmName("flatMapIterableTo")""") + } + typeParam("R") + typeParam("C : MutableCollection") + returns("C") + body { + """ + for (element in this) { + val list = transform(element) + destination.addAll(list) + } + return destination + """ + } + } + + val f_flatMapToSequence = fn("flatMapTo(destination: C, transform: (T) -> Sequence)") { + include(Sequences, Iterables, ArraysOfObjects, Maps) + } builder { + inline() + doc { "Appends all elements yielded from results of [transform] function being invoked on each ${f.element} of original ${f.collection}, to the given [destination]." } + if (f != Sequences) { + since("1.4") + annotation("@OptIn(kotlin.experimental.ExperimentalTypeInference::class)") + annotation("@OverloadResolutionByLambdaReturnType") + annotation("""@kotlin.jvm.JvmName("flatMapSequenceTo")""") } typeParam("R") typeParam("C : MutableCollection")