Q:

Use object array to generate a new array dynamically

I have a product form which I would like users to add variants of this product to. My object for the main settings
{
  title: 'Bike',
  settings: {
    options: [
      {
        title: 'Size',
        values: ['60cm', '80cm', '120cm']
      },
      {
        title: 'Color',
        values: ['White', 'Black', 'Red']
      }
    ],
    variants: []
  }
};

So for each option, I need a variant with each title and it's value - which will be regenerated and passed back to the .settings.variants above.
I have created a Stackblitz https://stackblitz.com/edit/angular-pi27mf
In this method I need to generate a brand new variants[], right now I only understand how to generate something like
{Size: "60cm", Color: "White", Image: "img.png"},
{Size: "60cm", Color: "Black", Image: "img.png"},
{Size: "60cm", Color: "Red", Image: "img.png"},

By just looping through one of the options.. but how can I loop though all options and create a new variant for each possible value?
The model I would need to achieve would be this
{Size: "60cm", Color: "White", Image: "img.png"},
{Size: "60cm", Color: "Black", Image: "img.png"},
{Size: "60cm", Color: "Red", Image: "img.png"},
{Size: "80cm", Color: "White", Image: "img.png"},
{Size: "80cm", Color: "Black", Image: "img.png"},
{Size: "80cm", Color: "Red", Image: "img.png"},
{Size: "120cm", Color: "White", Image: "img.png"},
{Size: "120cm", Color: "Black", Image: "img.png"},
{Size: "120cm", Color: "Red", Image: "img.png"},

Keeping in mind that Size and Color are dynamic and could be changed by the user, so the function can't just depend on this amount/type of options.
I am sure there is some array math that needs to be going on but if someone could point me in the right direction or re-create a stackblitz for me it would be a massive help.

A:

You could take the keys and values from data, generate the cartesian product from values and create key/value pairs as result.

var data = { title: 'Bike', settings: { options: [{ title: 'Size', values: ['60cm', '80cm', '120cm'] }, { title: 'Color', values: ['White', 'Black', 'Red'] }], variants: [] } },
    keys = data.settings.options.map(({ title }) => title).concat('Image'),
    values = data.settings.options.map(({ values }) => values).concat([['img.png']]),
    cartesian = values
        .reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []))
        .map(a => Object.assign({ title: a.slice(0, -1).join('/') }, ...keys.map((k, i) => ({ [k]: a[i] }))));

console.log(cartesian);
.as-console-wrapper { max-height: 100% !important; top: 0; }

