I'm a beginner, so please pardon my ignorance if this is trivial.
I have a javascript object of unknown length, and each property's value is an array (also of unknown length to me). For example:
var obj = {"varA":[1,2,3],
"varB":['good','bad'],
"varC":[0,100],
"varD":['low','med','high']
}
I want to loop through each of the properties and create a new object for every combination of property values. If I knew the number of properties I could just brute-forcedly use for loops, but is there a way to enumerate without knowing how many loops to hard-code?
I essentially want to do this kind of thing:
var oblist = [];
for (a in varA){
for (b in varB){
for (c in varC){
for (d in varD){
oblist.push({"varA":varA[a], "varB":varB[b], "varC":varC[c], "varD":varD[d]});
}
}
}
}
so that oblist will contain objects like:
{"varA":1, "varB":"good", "varC":0, "varD":"low"}
{"varA":1, "varB":"good", "varC":0, "varD":"med"}
...
{"varA":3, "varB":"bad", "varC":100, "varD":"high"}
Thanks!
Edit: Look I'm not asking for for-loop or indexing syntax help. I'm asking what to do if I don't know the number of properties in the object (e.g. varA, varB, varC, varD, varE, hell i could have varZZ for all i know), so I can't just hard-code 4 for loops. Is there a way to set that using obj[Object.keys(obj)[i]].length?
var obj = {"varA":[1,2,3],
"varB":['good','bad'],
"varC":[0,100],
"varD":['low','med','high']
}
// flatten the object into an array so it's easier to work with
var obj2list = function(obj) {
var list = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
list.push({
name: key,
val: obj[key]
});
}
}
return list;
};
// implement your favorite version of clone...this isn't particular fast
var cloneObj = function(obj) {
return JSON.parse(JSON.stringify(obj));
}
var iterateAndPopulateCombo = function(currentObj, listToIterate, result) {
if (listToIterate.length == 0) {
result.push(currentObj);
} else {
listToIterate[0].val.forEach(function(d) {
var newObj = cloneObj(currentObj);
newObj[listToIterate[0].name] = d;
iterateAndPopulateCombo(newObj, listToIterate.slice(1), result);
})
}
}
var list = obj2list(obj);
var result = [];
iterateAndPopulateCombo({}, list, result);
console.log(JSON.stringify(result));
document.body.appendChild(document.createTextNode(JSON.stringify(result)));
The combos you need are the cartesian product
of all the arrays within your obj, here's a fiddle showing it in action:
http://jsfiddle.net/sifriday/qmyxhhny/2/
And the code...
// I think the combo you're after is known as cartesian product
// Here's a function to do it, from:
// http://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript
// It needs Underscore.js
function cartesianProductOf() {
return _.reduce(arguments, function(a, b) {
return _.flatten(_.map(a, function(x) {
return _.map(b, function(y) {
return x.concat([y]);
});
}), true);
}, [ [] ]);
};
// Here's your object
var obj = {"varA":[1,2,3],
"varB":['good','bad'],
"varC":[0,100],
"varD":['low','med','high']
}
// Now I extract the arrays from your object
var idx, jdx, keys = Object.keys(obj), arrays = [], result1 = [], result2 = []
for (idx in keys) {
var key = keys[idx]
var arr = obj[key]
arrays.push(arr)
}
// We can calculate the combos of the obj, but this isn't annotated.
result1 = cartesianProductOf.apply(null, arrays)
// Now turn these back into annotated objects.
for (idx in result1) {
var tmp = result1[idx], obj = {}
for (jdx in tmp) {
obj[keys[jdx]] = tmp[jdx]
}
result2.push(obj)
}
// Done!
console.log(result2)
With a bit of effort I think this could be tidied up; you could probably ensure the annotation happens within the cartesian product.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With