diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt
index 6e84455e566..be8129d5a43 100644
--- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt
+++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt
@@ -1265,6 +1265,21 @@ public fun Iterable.fold(MA: Monoid): A =
public fun Iterable.foldMap(MB: Monoid, f: (A) -> B): B =
fold(MB.empty()) { acc, a -> MB.run { acc.combine(f(a)) } }
+/**
+ * Applies function [f] to each element
+ * and returns a list for the applied result Iterable.
+ *
+ * ```kotlin
+ * import arrow.core.crosswalk
+ * import io.kotest.matchers.shouldBe
+ *
+ * fun test() {
+ * val ints = listOf(1, 2)
+ * val res = ints.crosswalk { i -> listOf("a${i}", "b${i}", "c${i}") }
+ * res shouldBe listOf(listOf("a2", "a1"), listOf("b2", "b1"), listOf("c2", "c1"))
+ * }
+ * ```
+ */
public fun Iterable.crosswalk(f: (A) -> Iterable): List> =
fold(emptyList()) { bs, a ->
f(a).align(bs) { ior ->
@@ -1276,6 +1291,21 @@ public fun Iterable.crosswalk(f: (A) -> Iterable): List> =
}
}
+/**
+ * Applies function [f] to each element
+ * and returns the concatenated Map of the applied result Map.
+ *
+ * ```kotlin
+ * import arrow.core.crosswalk
+ * import io.kotest.matchers.shouldBe
+ *
+ * fun test() {
+ * val ints = listOf(1, 2)
+ * val res = ints.crosswalkMap { i -> mapOf("a" to i, "b" to i, "c" to i) }
+ * res shouldBe listOf("a", "b", "c").map { a -> a to ints.reversed() }.toMap()
+ * }
+ * ```
+ */
public fun Iterable.crosswalkMap(f: (A) -> Map): Map> =
fold(emptyMap()) { bs, a ->
f(a).align(bs) { (_, ior) ->
@@ -1287,6 +1317,21 @@ public fun Iterable.crosswalkMap(f: (A) -> Map): Map without null.
+ *
+ * ```kotlin
+ * import arrow.core.crosswalk
+ * import io.kotest.matchers.shouldBe
+ *
+ * fun test() {
+ * val ints = listOf(1, 2)
+ * val res = ints.crosswalkNull { i -> if (i % 2 == 0) "x${i}" else null }
+ * res shouldBe listOf("x2")
+ * }
+ * ```
+ */
public fun Iterable.crosswalkNull(f: (A) -> B?): List? =
fold?>(emptyList()) { bs, a ->
Ior.fromNullables(f(a), bs)?.fold(
diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt
index b2ceab7d9b1..f3bd57fd850 100644
--- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt
+++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt
@@ -17,6 +17,7 @@ import io.kotest.property.arbitrary.list
import io.kotest.property.arbitrary.orNull
import io.kotest.property.arbitrary.pair
import io.kotest.property.arbitrary.string
+import io.kotest.property.arbitrary.char
import io.kotest.property.checkAll
import kotlin.math.max
import kotlin.math.min
@@ -663,4 +664,32 @@ class IterableTest : StringSpec({
listOf(1,2,3).compareTo(listOf(1,1,3)) shouldBe 1
}
+ "crosswalk" {
+ checkAll(Arb.pair(Arb.list(Arb.int()), Arb.list(Arb.char()))) { (ints, chars) ->
+ val res = ints.crosswalk { i -> chars.map { c -> "${c}${i}" } }
+ val expected =
+ if (ints.isNotEmpty()) chars.map { c -> ints.reversed().map { i -> "${c}${i}" } }
+ else emptyList()
+ res shouldBe expected
+ }
+ }
+
+ "crosswalkMap" {
+ checkAll(Arb.pair(Arb.list(Arb.int()), Arb.list(Arb.char()))) { (ints, chars) ->
+ val res = ints.crosswalkMap { i -> chars.map { c -> c to i }.toMap() }
+ val expected =
+ if (ints.isNotEmpty()) chars.map { c -> c to ints.reversed() }.toMap()
+ else emptyMap()
+ res shouldBe expected
+ }
+ }
+
+ "crosswalkNull" {
+ checkAll(Arb.list(Arb.int())) { ints ->
+ val res = ints.crosswalkNull { i -> if (i % 2 == 0) "x${i}" else null }
+ val expected = ints.mapNotNull { i -> if (i % 2 == 0 ) "x${i}" else null }.reversed()
+ res shouldBe expected
+ }
+ }
+
})