Skip to content

Commit a3697c5

Browse files
committed
Add combinations.
1 parent 3e5e78d commit a3697c5

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import combineWithoutRepetitions from '../combineWithoutRepetitions';
2+
import factorial from '../../../math/factorial/factorial';
3+
4+
describe('combineWithoutRepetitions', () => {
5+
it('should combine string without repetitions', () => {
6+
expect(combineWithoutRepetitions('AB', 3)).toEqual([]);
7+
expect(combineWithoutRepetitions('AB', 1)).toEqual(['A', 'B']);
8+
expect(combineWithoutRepetitions('A', 1)).toEqual(['A']);
9+
expect(combineWithoutRepetitions('AB', 2)).toEqual(['AB']);
10+
expect(combineWithoutRepetitions('ABC', 2)).toEqual(['AB', 'AC', 'BC']);
11+
expect(combineWithoutRepetitions('ABC', 3)).toEqual(['ABC']);
12+
expect(combineWithoutRepetitions('ABCD', 3)).toEqual([
13+
'ABC',
14+
'ABD',
15+
'ACD',
16+
'BCD',
17+
]);
18+
expect(combineWithoutRepetitions('ABCDE', 3)).toEqual([
19+
'ABC',
20+
'ABD',
21+
'ABE',
22+
'ACD',
23+
'ACE',
24+
'ADE',
25+
'BCD',
26+
'BCE',
27+
'BDE',
28+
'CDE',
29+
]);
30+
31+
const combinationOptions = 'ABCDEFGH';
32+
const combinationSlotsNumber = 4;
33+
const combinations = combineWithoutRepetitions(combinationOptions, combinationSlotsNumber);
34+
const n = combinationOptions.length;
35+
const r = combinationSlotsNumber;
36+
const expectedNumberOfCombinations = factorial(n) / (factorial(r) * factorial(n - r));
37+
38+
expect(combinations.length).toBe(expectedNumberOfCombinations);
39+
});
40+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
@see: https://stackoverflow.com/a/127898/7794070
3+
4+
Lets say your array of letters looks like this: "ABCDEFGH".
5+
You have three indices (i, j, k) indicating which letters you
6+
are going to use for the current word, You start with:
7+
8+
A B C D E F G H
9+
^ ^ ^
10+
i j k
11+
12+
First you vary k, so the next step looks like that:
13+
14+
A B C D E F G H
15+
^ ^ ^
16+
i j k
17+
18+
If you reached the end you go on and vary j and then k again.
19+
20+
A B C D E F G H
21+
^ ^ ^
22+
i j k
23+
24+
A B C D E F G H
25+
^ ^ ^
26+
i j k
27+
28+
Once you j reached G you start also to vary i.
29+
30+
A B C D E F G H
31+
^ ^ ^
32+
i j k
33+
34+
A B C D E F G H
35+
^ ^ ^
36+
i j k
37+
...
38+
*/
39+
40+
/**
41+
* @param {string} combinationOptions
42+
* @param {number} combinationLength
43+
* @return {string[]}
44+
*/
45+
export default function combineWithoutRepetitions(combinationOptions, combinationLength) {
46+
// If combination length is just 1 then return combinationOptions.
47+
if (combinationLength === 1) {
48+
return Array.from(combinationOptions);
49+
}
50+
51+
// Init combinations array.
52+
const combinations = [];
53+
54+
for (let i = 0; i <= (combinationOptions.length - combinationLength); i += 1) {
55+
const smallerCombinations = combineWithoutRepetitions(
56+
combinationOptions.substr(i + 1),
57+
combinationLength - 1,
58+
);
59+
60+
for (let j = 0; j < smallerCombinations.length; j += 1) {
61+
combinations.push(combinationOptions[i] + smallerCombinations[j]);
62+
}
63+
}
64+
65+
// Return all calculated combinations.
66+
return combinations;
67+
}

0 commit comments

Comments
 (0)