eachCombination = function* eachCombination(set, length, prefix = []) {
if (length < 0 || length > set.length) {
throw new Error("length out of range");
}
if (length === 0) {
yield prefix;
} else if (set.length === length) {
yield [...prefix, ...set];
} else if (length === 1) {
for (const element of set) {
yield [...prefix, element];
}
} else {
// push the first element onto the prefix
// iterate combinations of length - 1 of the remaining set
// pop the first element off the prefix
// then iterate the remaining with combinations of length of the remaining set
// Recurse divvying up combos with pascal's rule C(n, k) == C(n-1, k-1) + C(n-1, k)
const [firstElement, ...remainingSet] = set;
prefix.push(firstElement);
for (const combination of eachCombination(
remainingSet,
length - 1,
prefix
)) {
yield combination;
}
prefix.pop();
for (const combination of eachCombination(
remainingSet,
length,
prefix
)) {
yield combination;
}
}
};