Last active
August 14, 2023 08:24
-
-
Save erikhuizinga/d2ca2b501864df219fd7f25e4dd000a4 to your computer and use it in GitHub Desktop.
Kotlin function for cartesian product of any number of sets of any size
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import kotlin.reflect.KFunction | |
typealias CartesianProduct = Set<List<*>> | |
/** | |
* Create the cartesian product of any number of sets of any size. Useful for parameterized tests | |
* to generate a large parameter space with little code. Note that any type information is lost, as | |
* the returned set contains list of any combination of types in the input set. | |
* | |
* @param a The first set. | |
* @param b The second set. | |
* @param sets Any additional sets. | |
*/ | |
fun cartesianProduct(a: Set<*>, b: Set<*>, vararg sets: Set<*>): CartesianProduct = | |
(setOf(a, b).plus(sets)) | |
.fold(listOf(listOf<Any?>())) { acc, set -> | |
acc.flatMap { list -> set.map { element -> list + element } } | |
} | |
.toSet() | |
/** | |
* Transform elements of a cartesian product. | |
*/ | |
fun <T> CartesianProduct.map(transform: KFunction<T>) = map { transform.call(*it.toTypedArray()) } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// JUnit 4 is a dependency for these imports | |
import org.junit.Assert.assertEquals | |
import org.junit.Assert.assertNotEquals | |
import org.junit.Assert.assertTrue | |
import org.junit.Test | |
class CartesianProductTest { | |
private val emptySet: Set<*> = emptySet<Nothing>() | |
private val a = setOf(1, 2) | |
private val b = setOf('3', '4') | |
@Test | |
fun `Given the empty set, When the cartesian product with itself is created, Then it returns the empty set`() { | |
assertTrue(cartesianProduct(emptySet, emptySet).isEmpty()) | |
} | |
@Test | |
fun `Given nonempty set A, When the cartesian product with the empty set is created, Then it returns the empty set`() { | |
assertTrue(cartesianProduct(a, emptySet).isEmpty()) | |
assertTrue(cartesianProduct(emptySet, a).isEmpty()) | |
} | |
@Test | |
fun `Given different nonempty sets A and B, When A x B and B x A are created, Then they are unequal`() = | |
assertNotEquals( | |
cartesianProduct(a, b), | |
cartesianProduct(b, a) | |
) | |
@Test | |
fun `Given different nonempty sets A, B, C and D, When A x B x C x D is created, Then it returns the correct cartesian product`() { | |
val c = setOf(5) | |
val d = setOf("6", "7", "8, 9") | |
val expected: CartesianProduct = setOf( | |
listOf(1, '3', 5, "6"), | |
listOf(1, '3', 5, "7"), | |
listOf(1, '3', 5, "8, 9"), | |
listOf(1, '4', 5, "6"), | |
listOf(1, '4', 5, "7"), | |
listOf(1, '4', 5, "8, 9"), | |
listOf(2, '3', 5, "6"), | |
listOf(2, '3', 5, "7"), | |
listOf(2, '3', 5, "8, 9"), | |
listOf(2, '4', 5, "6"), | |
listOf(2, '4', 5, "7"), | |
listOf(2, '4', 5, "8, 9") | |
) | |
val actual = cartesianProduct(a, b, c, d) | |
assertEquals(expected, actual) | |
} | |
@Test | |
fun `Given a parameter class and sets of input arguments, When the arguments' cartesian product is created and it is mapped to the parameter class, Then it returns the correct list of parameters`() = | |
assertEquals( | |
listOf( | |
Parameters(1, true), | |
Parameters(1, false), | |
Parameters(1, null), | |
Parameters(2, true), | |
Parameters(2, false), | |
Parameters(2, null) | |
), | |
cartesianProduct(a, setOf(true, false, null)) | |
.map(::Parameters) | |
) | |
internal data class Parameters(val number: Int, val maybe: Boolean?) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment