Skip to content

Commit dbf0b67

Browse files
committed
Optimize cross.
1 parent c4387a2 commit dbf0b67

File tree

3 files changed

+31
-16
lines changed

3 files changed

+31
-16
lines changed

.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"browser": true
1111
},
1212
"rules": {
13-
"no-cond-assign": 0
13+
"no-cond-assign": 0,
14+
"no-constant-condition": 0
1415
}
1516
}

src/cross.js

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1-
function* product(head, ...rest) {
2-
if (rest.length) {
3-
const tail = rest.pop();
4-
for (const p of product(head, ...rest)) {
5-
for (const t of tail) {
6-
yield [...p, t];
7-
}
8-
}
9-
} else {
10-
for (const h of head) {
11-
yield [h];
12-
}
13-
}
1+
function length(array) {
2+
return array.length;
3+
}
4+
5+
function arrayify(values) {
6+
return typeof values !== "object" || "length" in values ? values : Array.from(values);
147
}
158

169
function reducer(reduce) {
1710
return values => reduce(...values);
1811
}
1912

2013
export default function cross(...values) {
21-
const reduce = typeof values[values.length - 1] === "function" ? reducer(values.pop()) : undefined;
22-
return Array.from(product(...values), reduce);
14+
const reduce = typeof values[values.length - 1] === "function" && reducer(values.pop());
15+
values = values.map(arrayify);
16+
const lengths = values.map(length);
17+
const j = values.length - 1;
18+
const index = new Array(j + 1).fill(0);
19+
const product = [];
20+
while (true) {
21+
product.push(index.map((j, i) => values[i][j]));
22+
let i = j;
23+
while (++index[i] === lengths[i]) {
24+
if (i === 0) return reduce ? product.map(reduce) : product;
25+
index[i--] = 0;
26+
}
27+
}
2328
}

test/cross-test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,12 @@ tape("cross(a, b, c, f) invokes the specified function for each triple", functio
3333
test.deepEqual(arrays.cross([1, 2], [3, 4], [5, 6, 7], (a, b, c) => a + b + c), [9, 10, 11, 10, 11, 12, 10, 11, 12, 11, 12, 13]);
3434
test.end();
3535
});
36+
37+
tape("cross(a, b) returns Cartesian product a×b of generators", function(test) {
38+
test.deepEqual(arrays.cross(generate(1, 2), generate("x", "y")), [[1, "x"], [1, "y"], [2, "x"], [2, "y"]]);
39+
test.end();
40+
});
41+
42+
function* generate(...values) {
43+
yield* values;
44+
}

0 commit comments

Comments
 (0)