0

I have to make an array of all combinations with attribute values.

Attributes/values object:

let attributes = {
    color:    ['red', 'green', 'blue'],
    sizes:    ['sm', 'md', 'lg'],
    material: ['cotton', 'wool']
}

I need to make an array of all possible combinations as such.

color    sizes     material
red      sm        cotton
red      sm        wool
red      md        cotton
red      md        wool
red      lg        cotton
red      lg        wool
blue     sm        cotton
blue     sm        wool
blue     md        cotton
blue     md        wool
blue     lg        cotton
blue     lg        wool
green    sm        cotton
green    sm        wool
green    md        cotton
green    md        wool
green    lg        cotton
green    lg        wool

The attribute types and values count are both indefinite (at least 1). How can I achieve this?

This is the code I have so far

// keys = ['color', 'sizes', 'material']
// attributes is the same as above.

for (let keysIndex = 0; keysIndex < keys.length; keysIndex++) {
    let key = keys[i];
    let values = attributes[key];

    //loop through each value
    for (let valuesIndex = 0; valuesIndex < values.length; valuesIndex++) {

        // for each value, loop through all keys
        for (let keysIndex2 = 0; keysIndex2 < keys.length; keysIndex2++) {
            if (keysIndex === keysIndex2) {
                continue;
            }

            for (let valuesIndex2 = 0; valuesIndex2 < values.length; valuesIndex2++) {

                // What do I do from here?
                // Not sure if this is even the right path?

            }

        }

    }

}
Jeff
  • 141
  • 1
  • 9

1 Answers1

1

I took a two-step approach... first I extracted from the attributes object just an array of arrays ([['red', 'green', 'blue'], ['sm', ...], ...]). Then I computed the product of those arrays recursively. Then I put them back into objects with the appropriate keys.

let attributes = {
    color:    ['red', 'green', 'blue'],
    sizes:    ['sm', 'md', 'lg'],
    material: ['cotton', 'wool']
};

let getProducts = (arrays) => {
    if (arrays.length === 0) {
        return [[]];
    }

    let results = [];

    getProducts(arrays.slice(1)).forEach((product) => {
        arrays[0].forEach((value) => {
            results.push([value].concat(product));
        });
    });

    return results;
};

let getAllCombinations = (attributes) => {
    let attributeNames = Object.keys(attributes);

    let attributeValues = attributeNames.map((name) => attributes[name]);

    return getProducts(attributeValues).map((product) => {
        obj = {};
        attributeNames.forEach((name, i) => {
            obj[name] = product[i];
        });
        return obj;
    });
};

console.log(getAllCombinations(attributes));

// Output:
// [ { color: 'red', sizes: 'sm', material: 'cotton' },
//   { color: 'green', sizes: 'sm', material: 'cotton' },
//   { color: 'blue', sizes: 'sm', material: 'cotton' },
//   { color: 'red', sizes: 'md', material: 'cotton' },
//   { color: 'green', sizes: 'md', material: 'cotton' },
//   { color: 'blue', sizes: 'md', material: 'cotton' },
//   { color: 'red', sizes: 'lg', material: 'cotton' },
//   { color: 'green', sizes: 'lg', material: 'cotton' },
//   { color: 'blue', sizes: 'lg', material: 'cotton' },
//   { color: 'red', sizes: 'sm', material: 'wool' },
//   { color: 'green', sizes: 'sm', material: 'wool' },
//   { color: 'blue', sizes: 'sm', material: 'wool' },
//   { color: 'red', sizes: 'md', material: 'wool' },
//   { color: 'green', sizes: 'md', material: 'wool' },
//   { color: 'blue', sizes: 'md', material: 'wool' },
//   { color: 'red', sizes: 'lg', material: 'wool' },
//   { color: 'green', sizes: 'lg', material: 'wool' },
//   { color: 'blue', sizes: 'lg', material: 'wool' } ]
user94559
  • 59,196
  • 6
  • 103
  • 103
  • Works perfectly. Thanks for you help. Any tips on how I can figure things out like this better? I was never good with algorithms. – Jeff Aug 01 '16 at 21:39
  • 2
    I think the key to something like this is to recognize that recursion will be the most natural way to solve it. You *could* write three for loops for the three attributes you have, but that doesn't work if you don't know in advance how many attributes there will be. Breaking the problem down into "Let's pick a color first and then pick all the rest" makes the algorithm a lot simpler. (Pick value for first attribute; call recursively to get the rest of the attributes.) Finally, you just have to decide on a base case. (When there's no more "rest of the attributes," just return.) – user94559 Aug 01 '16 at 22:28