I have an array of objects with names and options and I need all possible combinations of products. The important part is that this array has an N number of objects with N number of options in every object.
I tried to create some kind of recursive algorithm, but the problem is that I failed to push recursively to receive the needed data structure in the end. I also tried the approach from Cartesian product of multiple arrays in JavaScript but it seems it is not relevant to the output needed.
Example:
input = [
{
name: "Size",
options: [ { value: "S" }, { value: "M" }, { value: "L" }, ...and so on]
},
{
name: "Color",
options: [ { value: "Red" }, { value: "White" }, { value: "Blue" }, ...and so on]
},
{
name: "Weight",
options: [ { value: "1kg" }, { value: "2kg" }, { value: "3kg" }, { value: "4kg"}, ]
},
.... and so on
];
I need to have all the possible combinations in the form of the array which itself includes an array of objects with the object's name and value.
Example (Array of arrays):
output = [
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '1kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '2kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '3kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '4kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '1kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '2kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '3kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '4kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Blue'}, {name: 'Weight', value: '1kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Blue'}, {name: 'Weight', value: '2kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Blue'}, {name: 'Weight', value: '3kg'} ],
[ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Blue'}, {name: 'Weight', value: '4kg'} ],
[ {name: 'Size', value: 'M'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '1kg'} ],
[ {name: 'Size', value: 'M'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '2kg'} ],
[ {name: 'Size', value: 'M'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '3kg'} ],
[ {name: 'Size', value: 'M'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '4kg'} ],
[ {name: 'Size', value: 'M'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '1kg'} ],
[ {name: 'Size', value: 'M'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '2kg'} ],
[ {name: 'Size', value: 'M'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '3kg'} ],
[ {name: 'Size', value: 'M'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '4kg'} ],
... and so on
];
CodePudding user response:
This is definitely a cartesian product question. The only thing is that you will need to format your input before you call the cartesian product. Here is one version, using a simple, recursive cartesian
function.
const cartesian = ([xs, ...xss]) =>
xs = undefined
? []
: xss.length == 0
? xs .map (x => [x])
: xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))
const combine = (properties) =>
cartesian (properties .map (({name, options}) => options .map (({value}) => ({name, value}))))
const properties = [{name: "Size", options: [ { value: "S" }, { value: "M" }, { value: "L" }, /*...and so on */]}, {name: "Color", options: [ { value: "Red" }, { value: "White" }, { value: "Blue" }, /*...and so on */]}, {name: "Weight", options: [ { value: "1kg" }, { value: "2kg" }, { value: "3kg" }, { value: "4kg"}, ]}, /* .... and so on */]
console .log (JSON .stringify (combine (properties), null, 2))
.as-console-wrapper {max-height: 100% !important; top: 0}
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
But I find your output format quite repetitive. I would prefer something like:
[
{Size: "S", Color: "Red", Weight: "1kg"},
{Size: "S", Color: "Red", Weight: "2kg"},
{Size: "S", Color: "Red", Weight: "3kg"},
{Size: "S", Color: "Red", Weight: "4kg"},
{Size: "S", Color: "White", Weight: "1kg"},
// ...
{Size: "L", Color: "Blue", Weight: "4kg"},
]
and we can achieve it with just a bit more work:
const cartesian = ([xs, ...xss]) =>
xs = undefined
? []
: xss.length == 0
? xs .map (x => [x])
: xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))
const properties = [{name: "Size", options: [ { value: "S" }, { value: "M" }, { value: "L" }, /*...and so on */]}, {name: "Color", options: [ { value: "Red" }, { value: "White" }, { value: "Blue" }, /*...and so on */]}, {name: "Weight", options: [ { value: "1kg" }, { value: "2kg" }, { value: "3kg" }, { value: "4kg"}, ]}, /* .... and so on */]
const combine = (properties) =>
cartesian (properties .map (
({name, options}) => options .map (({value}) => ({[name]: value}))
)) .map (ps => Object .assign ({}, ...ps))
console .log (combine (properties))
.as-console-wrapper {max-height: 100% !important; top: 0}
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
If that version of the cartesian product does not make sense to you, this one is an alternative:
const cartesian = ([x, ...xs]) =>
(xs || []) .reduce (
(a, b) => a .reduce (
(c, d) => [... c, ... (b .map (e => [... d, e]))],
[]
),
(x || []) .map (x => [x])
)