Skip to content

Commit 0af06d6

Browse files
committed
Add combinations.
1 parent a3697c5 commit 0af06d6

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import combineWithRepetitions from '../combineWithRepetitions';
2+
import factorial from '../../../math/factorial/factorial';
3+
4+
describe('combineWithRepetitions', () => {
5+
it('should combine string with repetitions', () => {
6+
expect(combineWithRepetitions(['A'], 1)).toEqual([
7+
['A'],
8+
]);
9+
10+
expect(combineWithRepetitions(['A', 'B'], 1)).toEqual([
11+
['A'],
12+
['B'],
13+
]);
14+
15+
expect(combineWithRepetitions(['A', 'B'], 2)).toEqual([
16+
['A', 'A'],
17+
['A', 'B'],
18+
['B', 'B'],
19+
]);
20+
21+
expect(combineWithRepetitions(['A', 'B'], 3)).toEqual([
22+
['A', 'A', 'A'],
23+
['A', 'A', 'B'],
24+
['A', 'B', 'B'],
25+
['B', 'B', 'B'],
26+
]);
27+
28+
expect(combineWithRepetitions(['A', 'B', 'C'], 2)).toEqual([
29+
['A', 'A'],
30+
['A', 'B'],
31+
['A', 'C'],
32+
['B', 'B'],
33+
['B', 'C'],
34+
['C', 'C'],
35+
]);
36+
37+
expect(combineWithRepetitions(['A', 'B', 'C'], 3)).toEqual([
38+
['A', 'A', 'A'],
39+
['A', 'A', 'B'],
40+
['A', 'A', 'C'],
41+
['A', 'B', 'B'],
42+
['A', 'B', 'C'],
43+
['A', 'C', 'C'],
44+
['B', 'B', 'B'],
45+
['B', 'B', 'C'],
46+
['B', 'C', 'C'],
47+
['C', 'C', 'C'],
48+
]);
49+
50+
const combinationOptions = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
51+
const combinationSlotsNumber = 4;
52+
const combinations = combineWithRepetitions(combinationOptions, combinationSlotsNumber);
53+
const n = combinationOptions.length;
54+
const r = combinationSlotsNumber;
55+
const expectedNumberOfCombinations = factorial((r + n) - 1) / (factorial(r) * factorial(n - 1));
56+
57+
expect(combinations.length).toBe(expectedNumberOfCombinations);
58+
});
59+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @param {*[]} combinationOptions
3+
* @param {number} combinationLength
4+
* @return {*[]}
5+
*/
6+
7+
export default function combineWithRepetitions(combinationOptions, combinationLength) {
8+
// If combination length equal to 0 then return empty combination.
9+
if (combinationLength === 0) {
10+
return [[]];
11+
}
12+
13+
// If combination options are empty then return "no-combinations" array.
14+
if (combinationOptions.length === 0) {
15+
return [];
16+
}
17+
18+
// Init combinations array.
19+
const combos = [];
20+
21+
// Find all shorter combinations and attach head to each of those.
22+
const headCombo = [combinationOptions[0]];
23+
const shorterCombos = combineWithRepetitions(combinationOptions, combinationLength - 1);
24+
25+
for (let combinationIndex = 0; combinationIndex < shorterCombos.length; combinationIndex += 1) {
26+
const combo = headCombo.concat(shorterCombos[combinationIndex]);
27+
combos.push(combo);
28+
}
29+
30+
// Let's shift head to the right and calculate all the rest combinations.
31+
const combinationsWithoutHead = combineWithRepetitions(
32+
combinationOptions.slice(1),
33+
combinationLength,
34+
);
35+
36+
// Join all combinations and return them.
37+
return combos.concat(combinationsWithoutHead);
38+
}

0 commit comments

Comments
 (0)