Cartesian product of javascript object properties

I have an object of the following form (simplified test case below)

var test = { shirts: { sizes: ['large', 'medium'] ,colors:['red', 'blue'] } , trousers: { type: ['formal', 'casual'] , pattern: ['plaid', 'stripes'] } }; 

I want to create a Cartesian product of properties so that the output is an array of the following form:

 // desired output [ {shirts:{sizes:'large', color:'red'}, trousers:{type:'formal', pattern:'plaid'}} ,{shirts:{sizes:'large', color:'red'}, trousers:{type:'formal', pattern:'stripes'}} ,{shirts:{sizes:'large', color:'red'}, trousers:{type:'casual', pattern:'plaid'}} , {shirts:{sizes:'large', color:'red'}, trousers:{type:'casual', pattern:'stripes'}} ,{shirts:{sizes:'large', color:'blue'}, trousers:{type:'formal', pattern:'plaid'}} ..... and so on ] 

How can i achieve this? I developed the following code (based on modifying the code for the Cartesian product of an array from another SO message), but it looks like I'm getting attached to nodes trying to get this to work.

  function myCartesianProduct(input, current) { if (!input) { return []; } var head = input[Object.keys(input)[0]]; var tail = objSlice(input); var output = []; for (var key in head) { for (var i = 0; i < head[key].length; i++) { var newCurrent = copy(current); newCurrent[key] = head[key][i]; if (Object.keys(tail).length) { //if tail.length var productOfTail = myCartesianProduct(tail, newCurrent); output = output.concat(productOfTail); } else { output.push(newCurrent); } } } return output; } function objSlice(obj) { var slicedObj = angular.copy(obj); // copy object using angularJs copy method delete slicedObj[Object.keys(slicedObj)[0]]; //delete the first key return slicedObj; }; function copy(obj) { var res = {}; for (var p in obj) res[p] = obj[p]; return res; } console.log(myCartesianProduct(test)); 

Thanks in advance for your help!

+5
source share
1 answer

Ok, let's start with the function that generates the product of the given arrays:

 function product(args) { if(!args.length) return [[]]; var prod = product(args.slice(1)), r = []; args[0].forEach(function(x) { prod.forEach(function(p) { r.push([x].concat(p)); }); }); return r; } 

The following uses product to convert something like {a:[1,2], b:[3,4]} to [{a:1,b:3},{a:1,b:4},{a:2,b:3},{a:2,b:4}] :

 function objectProduct(obj) { var keys = Object.keys(obj), values = keys.map(function(x) { return obj[x] }); return product(values).map(function(p) { var e = {}; keys.forEach(function(k, n) { e[k] = p[n] }); return e; }); } 

For your test data, you must apply them twice:

 var result = {}; Object.keys(test).forEach(function(k) { result[k] = objectProduct(test[k]) }); result = objectProduct(result); 

This will give you the desired result.

+6
source

Source: https://habr.com/ru/post/1206044/


All Articles