Skip to content

Instantly share code, notes, and snippets.

@spacecowboy
Last active May 13, 2020 08:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spacecowboy/5749ba82227a5ce410f93c9dd2d46cc4 to your computer and use it in GitHub Desktop.
Save spacecowboy/5749ba82227a5ce410f93c9dd2d46cc4 to your computer and use it in GitHub Desktop.
Calculates the cartesian product of an arbitrary number of lists
/**
Calculates the cartesian product of an arbitrary number of lists.
Given `[[1, 2, 3], ["a", "b"]]`, it will produce
```
[
[1, "a"],
[2, "a"],
[3, "a"],
[1, "b"],
[2, "b"],
[3, "b"],
]
```
Useful if you have a parameterized test which needs to test all combinations of all arguments.
*/
fun cartesianProduct(args: List<List<Any?>>): List<List<Any?>> {
return cartesianProductRec(
args = args,
indices = List(args.count()) { 0 },
product = mutableListOf()
)
}
tailrec fun cartesianProductRec(
args: List<List<Any?>>,
indices: List<Int>,
product: MutableList<List<Any?>>
): List<List<Any?>> {
val arguments = mutableListOf<Any?>()
val nextIndices = mutableListOf<Int>()
// Increase index by this much
var carry = 1
for ((index, arg) in indices.zip(args)) {
arguments.add(arg[index])
val nextIndex = if (carry == 0) {
index
} else {
if (index + carry > arg.lastIndex) {
0.also {
carry = index + carry - arg.lastIndex
}
} else {
(index + carry).also {
carry = 0
}
}
}
nextIndices.add(nextIndex)
}
product.add(arguments)
return if (carry == 1) {
// We're done - no more indices possible to iterate
product
} else {
cartesianProductRec(args, nextIndices, product)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment