Skip to content

Instantly share code, notes, and snippets.

@cybercase
Last active February 10, 2023 10:59
Show Gist options
  • Save cybercase/db7dde901d7070c98c48 to your computer and use it in GitHub Desktop.
Save cybercase/db7dde901d7070c98c48 to your computer and use it in GitHub Desktop.
Python-like itertools.product function in javascript
function product() {
var args = Array.prototype.slice.call(arguments); // makes array from arguments
return args.reduce(function tl (accumulator, value) {
var tmp = [];
accumulator.forEach(function (a0) {
value.forEach(function (a1) {
tmp.push(a0.concat(a1));
});
});
return tmp;
}, [[]]);
}
console.log(product([1], [2, 3], ['a', 'b']));
@sponrad
Copy link

sponrad commented Dec 29, 2021

This is fantastic... it took me a little while to understand it so I rewrote the original with descriptive variable names and updated some syntax for ES6.

function arrayProduct(...arrays) {
    return arrays.reduce((prevAccumulator, currentArray) => {
        let newAccumulator = [];
        prevAccumulator.forEach(prevAccumulatorArray => {
            currentArray.forEach(currentValue => {
                newAccumulator.push(prevAccumulatorArray.concat(currentValue));
            });
        });
        return newAccumulator;
    }, [[]]);
}

@Hoppingmad9
Copy link

Doing arrays[0] (for @sponrad 's answer) or args[0] (for @cybercase 's) allows you to use Object.values(x) as the input. Useful if you don't know how many arrays are in your object/array.

function arrayProduct(...arrays) {
    return arrays[0].reduce((prevAccumulator, currentArray) => {
        let newAccumulator = [];
        prevAccumulator.forEach(prevAccumulatorArray => {
            currentArray.forEach(currentValue => {
                newAccumulator.push(prevAccumulatorArray.concat(currentValue));
            });
        });
        return newAccumulator;
    }, [[]]);
}

const packet = {
    'weight': ['w1', 'w2', 'w3', 'w4'],
    'speed': ['s1', 's2', 's3'],
    'colour': ['c1', 'c2'],
}

console.log(arrayProduct(Object.values(packet)));
// [["w1", "s1", "c1"], ["w1", "s1", "c2"], ["w1", "s2", "c1"], ["w1", "s2", "c2"], ["w1", "s3", "c1"], ["w1", "s3", "c2"], 
// ["w2", "s1", "c1"], ["w2", "s1", "c2"], ["w2", "s2", "c1"], ["w2", "s2", "c2"], ["w2", "s3", "c1"], ["w2", "s3", "c2"], 
// ["w3", "s1", "c1"], ["w3", "s1", "c2"], ["w3", "s2", "c1"], ["w3", "s2", "c2"],  ["w3", "s3", "c1"], ["w3", "s3", "c2"], 
// ["w4", "s1", "c1"], ["w4", "s1", "c2"], ["w4", "s2", "c1"], ["w4", "s2", "c2"], ["w4", "s3", "c1"], ["w4", "s3", "c2"]]

@gaspardfeuvray
Copy link

gaspardfeuvray commented Jun 11, 2022

Generic version for Typescript:

const combinations = <T>(sets: T[][]): T[][] => {
  if (sets.length === 1) {
    return sets[0].map((el) => [el]);
  } else
    return sets[0].flatMap((val) =>
      combinations(sets.slice(1)).map((c): T[] => [val].concat(c))
    );
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment