Skip to content

Commit 43768d3

Browse files
♻️ refactor: Extract indices generator.
1 parent 50fb60c commit 43768d3

File tree

3 files changed

+62
-51
lines changed

3 files changed

+62
-51
lines changed

src/_permutations.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import assert from 'assert';
2+
import {_take} from '@iterable-iterator/slice';
3+
4+
import {list} from '@iterable-iterator/list';
5+
import {
6+
forwardRangeIterator,
7+
backwardRangeIterator,
8+
} from '@iterable-iterator/range';
9+
10+
/**
11+
* Yields all k-permutations of {0, 1, ..., n-1}.
12+
*
13+
* @param {number} n
14+
* @param {number} k
15+
* @returns {IterableIterator<IterableIterator<number>>}
16+
*/
17+
export default function* _permutations(n, k) {
18+
assert(Number.isInteger(k) && k >= 0);
19+
if (k > n) return;
20+
21+
const indices = list(forwardRangeIterator(0, n, 1));
22+
const cycles = list(backwardRangeIterator(n, n - k, -1));
23+
24+
yield _take(indices, k);
25+
26+
if (k === 0 || n === 0) return;
27+
28+
while (true) {
29+
let i = k;
30+
31+
while (i--) {
32+
--cycles[i];
33+
34+
if (cycles[i] === 0) {
35+
// Could be costly
36+
indices.push(indices.splice(i, 1)[0]);
37+
38+
cycles[i] = n - i;
39+
} else {
40+
const j = cycles[i];
41+
42+
[indices[i], indices[n - j]] = [indices[n - j], indices[i]];
43+
44+
yield _take(indices, k);
45+
break;
46+
}
47+
}
48+
49+
if (i === -1) return;
50+
}
51+
}

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export {default as _permutations} from './_permutations.js';
12
export {default as permutations} from './permutations.js';

src/permutations.js

Lines changed: 10 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
import assert from 'assert';
2-
import {_take} from '@iterable-iterator/slice';
3-
41
import {list} from '@iterable-iterator/list';
5-
import {pick} from '@iterable-iterator/map';
6-
import {
7-
forwardRangeIterator,
8-
backwardRangeIterator,
9-
} from '@iterable-iterator/range';
2+
import {map, pick} from '@iterable-iterator/map';
3+
4+
import _permutations from './_permutations.js';
105

116
/**
127
* Yields all permutations of each possible choice of <code>r</code> elements
@@ -24,48 +19,12 @@ import {
2419
* @param {number} r - The size of the permutations to generate.
2520
* @returns {IterableIterator}
2621
*/
27-
export default function* permutations(iterable, r) {
28-
assert(Number.isInteger(r) && r >= 0);
22+
const permutations = (iterable, r) => {
2923
const pool = list(iterable);
24+
return map(
25+
(indices) => list(pick(pool, indices)),
26+
_permutations(pool.length, r),
27+
);
28+
};
3029

31-
const length = pool.length;
32-
33-
if (r > length) {
34-
return;
35-
}
36-
37-
const indices = list(forwardRangeIterator(0, length, 1));
38-
const cycles = list(backwardRangeIterator(length, length - r, -1));
39-
40-
yield list(pick(pool, _take(indices, r)));
41-
42-
if (r === 0 || length === 0) {
43-
return;
44-
}
45-
46-
while (true) {
47-
let i = r;
48-
49-
while (i--) {
50-
--cycles[i];
51-
52-
if (cycles[i] === 0) {
53-
// Could be costly
54-
indices.push(indices.splice(i, 1)[0]);
55-
56-
cycles[i] = length - i;
57-
} else {
58-
const j = cycles[i];
59-
60-
[indices[i], indices[length - j]] = [indices[length - j], indices[i]];
61-
62-
yield list(pick(pool, _take(indices, r)));
63-
break;
64-
}
65-
}
66-
67-
if (i === -1) {
68-
return;
69-
}
70-
}
71-
}
30+
export default permutations;

0 commit comments

Comments
 (0)