admin管理员组文章数量:1335403
I need to generate a plete set of variants based on a list of N attributes, while keeping the attribute name intact.
var input = [
{ 'colour' : ['red', 'green'] },
{ 'material' : ['cotton', 'wool', 'silk'] },
{ 'shape' : ['round', 'square', 'rectangle'] }
];
var expected = [
{ 'colour': 'red', 'material': 'cotton', 'shape': 'round' },
{ 'colour': 'red', 'material': 'cotton', 'shape': 'square' },
{ 'colour': 'red', 'material': 'cotton', 'shape': 'rectangle' },
{ 'colour': 'red', 'material': 'wool', 'shape': 'round' },
{ 'colour': 'red', 'material': 'wool', 'shape': 'square' },
{ 'colour': 'red', 'material': 'wool', 'shape': 'rectangle' },
{ 'colour': 'red', 'material': 'silk', 'shape': 'round' },
{ 'colour': 'red', 'material': 'silk', 'shape': 'square' },
{ 'colour': 'red', 'material': 'silk', 'shape': 'rectangle' },
{ 'colour': 'green', 'material': 'cotton', 'shape': 'round' },
{ 'colour': 'green', 'material': 'cotton', 'shape': 'square' },
{ 'colour': 'green', 'material': 'cotton', 'shape': 'rectangle' },
{ 'colour': 'green', 'material': 'wool', 'shape': 'round' },
{ 'colour': 'green', 'material': 'wool', 'shape': 'square' },
{ 'colour': 'green', 'material': 'wool', 'shape': 'rectangle' },
{ 'colour': 'green', 'material': 'silk', 'shape': 'round' },
{ 'colour': 'green', 'material': 'silk', 'shape': 'square' },
{ 'colour': 'green', 'material': 'silk', 'shape': 'rectangle' }
];
There are lots of algorithms around for cartesian products of arrays, but I can't seem to find one for objects that preserves the keys.
Performance isn't a massive concern as there will never be more than a dozen or so values for each attribute. The order doesn't have to exactly match expected
.
I've made an initial attempt based on the standard algorithms for lists, but I'm struggling:
function cartesianProduct(input, current) {
if (!input || input.length < 1) {
return [];
}
var head = input[0];
var tail = input.slice(1);
var output = [];
for (var key in head) {
for (var i = 0; i < head[key].length; i++) {
if (typeof current == 'undefined') {
var current = {};
}
current[key] = head[key][i];
var productOfTail = cartesianProduct(tail, current);
output.push(current);
console.log(current);
}
}
return output;
}
console.log(cartesianProduct(input));
I need to generate a plete set of variants based on a list of N attributes, while keeping the attribute name intact.
var input = [
{ 'colour' : ['red', 'green'] },
{ 'material' : ['cotton', 'wool', 'silk'] },
{ 'shape' : ['round', 'square', 'rectangle'] }
];
var expected = [
{ 'colour': 'red', 'material': 'cotton', 'shape': 'round' },
{ 'colour': 'red', 'material': 'cotton', 'shape': 'square' },
{ 'colour': 'red', 'material': 'cotton', 'shape': 'rectangle' },
{ 'colour': 'red', 'material': 'wool', 'shape': 'round' },
{ 'colour': 'red', 'material': 'wool', 'shape': 'square' },
{ 'colour': 'red', 'material': 'wool', 'shape': 'rectangle' },
{ 'colour': 'red', 'material': 'silk', 'shape': 'round' },
{ 'colour': 'red', 'material': 'silk', 'shape': 'square' },
{ 'colour': 'red', 'material': 'silk', 'shape': 'rectangle' },
{ 'colour': 'green', 'material': 'cotton', 'shape': 'round' },
{ 'colour': 'green', 'material': 'cotton', 'shape': 'square' },
{ 'colour': 'green', 'material': 'cotton', 'shape': 'rectangle' },
{ 'colour': 'green', 'material': 'wool', 'shape': 'round' },
{ 'colour': 'green', 'material': 'wool', 'shape': 'square' },
{ 'colour': 'green', 'material': 'wool', 'shape': 'rectangle' },
{ 'colour': 'green', 'material': 'silk', 'shape': 'round' },
{ 'colour': 'green', 'material': 'silk', 'shape': 'square' },
{ 'colour': 'green', 'material': 'silk', 'shape': 'rectangle' }
];
There are lots of algorithms around for cartesian products of arrays, but I can't seem to find one for objects that preserves the keys.
Performance isn't a massive concern as there will never be more than a dozen or so values for each attribute. The order doesn't have to exactly match expected
.
I've made an initial attempt based on the standard algorithms for lists, but I'm struggling:
function cartesianProduct(input, current) {
if (!input || input.length < 1) {
return [];
}
var head = input[0];
var tail = input.slice(1);
var output = [];
for (var key in head) {
for (var i = 0; i < head[key].length; i++) {
if (typeof current == 'undefined') {
var current = {};
}
current[key] = head[key][i];
var productOfTail = cartesianProduct(tail, current);
output.push(current);
console.log(current);
}
}
return output;
}
console.log(cartesianProduct(input));
Share
Improve this question
edited Sep 23, 2013 at 12:45
Phil Moorhouse
asked Sep 23, 2013 at 11:20
Phil MoorhousePhil Moorhouse
87010 silver badges22 bronze badges
1
- there is a big issue in your code : you don't declare i as a var, so it is considered a global var, hence changed in the inner function calls... – GameAlchemist Commented Sep 23, 2013 at 12:40
2 Answers
Reset to default 7Once you get rid of the ' 'i' is a global var issue', you can get to the result with this code for instance :
var input = [
{ 'colour' : ['red', 'green'] },
{ 'material' : ['cotton', 'wool', 'silk'] },
{ 'shape' : ['round', 'square', 'rectangle'] }
];
function cartesianProduct(input, current) {
if (!input || !input.length) { return []; }
var head = input[0];
var tail = input.slice(1);
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 (tail.length) {
var productOfTail =
cartesianProduct(tail, newCurrent);
output = output.concat(productOfTail);
} else output.push(newCurrent);
}
}
return output;
}
function copy(obj) {
var res = {};
for (var p in obj) res[p] = obj[p];
return res;
}
console.log(cartesianProduct(input));
Heres a solution using Ramda.js
const input = [
{
'colour': ['red', 'green']
},
{
'material': ['cotton', 'wool', 'silk']
},
{
'shape': ['round', 'square', 'rectangle']
}
]
const cartesianProductList = (Xs) => (
R.reduce(
(Ys, X) => (
R.map(R.apply(R.append), R.xprod(X, Ys))
),
[[]],
Xs
)
)
const xPairs = (x, xs) => (
R.map(R.pair(x), xs)
)
const cartesianProductObject = (objs) => (
R.pipe(
R.mergeAll,
R.toPairs,
R.map(R.apply(xPairs)),
cartesianProductList,
R.map(R.fromPairs),
)(objs)
)
console.log(cartesianProductObject(input))
<script src="//cdnjs.cloudflare./ajax/libs/ramda/0.25.0/ramda.min.js"></script>
本文标签: setCartesian product of objects in javascriptStack Overflow
版权声明:本文标题:set - Cartesian product of objects in javascript - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742372273a2462454.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论