问题
I have found many solutions about all posible combinations between array values but I need something different, I hope you could support me. Basically is to create all posible objects that combine array keys with true|false values, something like this:
Input: (Should return an array of 32 objects, 2exp5, two posible values in 5 keys)
let properties = ['arm','lens','season','food','size'];
Output:
let combinations = [
{"arm": "false","lens": "false","season": "false","food": "false","size": "false"}
{"arm": "false","lens": "false","season": "false","food": "false","size": "true"}
{"arm": "false","lens": "false","season": "false","food": "true","size": "true"}
{"arm": "false","lens": "false","season": "true","food": "true","size": "true"}
{"arm": "false","lens": "true","season": "true","food": "true","size": "true"}
{"arm": "true","lens": "true","season": "true","food": "true","size": "true"}
{"arm": "true","lens": "true","season": "true","food": "false","size": "true"}
{"arm": "true","lens": "true","season": "false","food": "false","size": "true"}
and so on...
]
Thank you so much!
回答1:
You could use a 2D matrix with on and off switches for each property. Then create the entries for each key and create the object using Object.fromEntries()
0 0 0 0 0
0 0 0 0 1
0 0 0 1 0
0 0 0 1 1
etc
You need a total of
2 ** keys.lengthobjects in the array. So, create that usingArray.from({ 2 ** keys.length })In the map function, create a binary number for the current row using
row.toString(2)Add the leading 0s until the string is
keys.lengthlong: ("00001")split the string to make it an array (
["0", "0", "0", "0", "1"])map this array and create an array of entries for the corresponding key
[["arm","false"],["lens","false"],["season","false"],["food","false"],["size","false"]]Create an object from the array of entries using Object.fromEntries()
Here's a snippet:
let keys = ['arm', 'lens', 'season', 'food', 'size'];
function combination(keys) {
return Array.from({ length: 2 ** keys.length }, (_, row) =>
Object.fromEntries(
row.toString(2)
.padStart(keys.length, 0)
.split('')
.map((binary, j) => [keys[j], String(Boolean(+binary))])
)
)
}
console.log(combination(keys))
回答2:
We can build this on two reusable functions, like this:
const crossproduct = (xss) =>
xss.reduce ((ps, xs) => ps.reduce ((r, p) => [...r, ...(xs.map (x => [...p, x]))], []), [[]])
const mergeAll = xs =>
Object.assign ({}, ...xs)
const combine = (properties, values) =>
crossproduct (properties.map (p => values.map (v => ({[p]: v}))))
.map (mergeAll)
const properties = ['arm', 'lens', 'season', 'food', 'size']
const values = [true, false]
console.log (combine (properties, values))
.as-console-wrapper {min-height: 100% !important; top: 0}
Our utility functions are:
crossproduct, which takes an array of arrays and finds the cartesian product of them. For instance,crossproduct ([['a', 'b', 'c'], [1, 2], ['x', 'y']]) //=> [["a", 1, "x"], ["a", 1, "y"], ["a", 2, "x"], ["a", 2, "y"], // ["b", 1, "x"], ["b", 1, "y"], ["b", 2, "x"], ["b", 2, "y"], // ["c", 1, "x"], ["c", 1, "y"], ["c", 2, "x"], ["c", 2, "y"]]and
mergeAll, which combines an array of separate objects into one like this:mergeAll ([{foo: 1}, {bar: 2}, {baz: 3}]) //=> {foo: 1, bar: 2, baz: 3}This is just a thin wrapper around
Object.assign, simply applying it to an array rather than individual objects.
Our main function, combine first creates an array of arrays matching individual property names to values like this:
[
[{"arm": true}, {"arm": false}],
[{"lens": true}, {"lens": false}],
[{"season": true}, {"season": false}],
[{"food": true}, {"food": false}],
[{"size": true}, {"size": false}]
]
This is the bit properties.map (p => values.map (v => ({[p]: v}))). While that could be extracted as a stand-alone function, it doesn't seem to have any other uses, so the decision is simply a matter of code aesthetics.
We call crossproduct on that result, getting this intermediate format:
[
[{"arm": true}, {"lens": true}, {"season": true}, {"food": true}, {"size": true}],
[{"arm": true}, {"lens": true}, {"season": true}, {"food": true}, {"size": false}],
[{"arm": true}, {"lens": true}, {"season": true}, {"food": false}, {"size": true}],
[{"arm": true}, {"lens": true}, {"season": true}, {"food": false}, {"size": false}],
// ...
[{"arm": false}, {"lens": false}, {"season": false}, {"food": false}, {"size": false}]
]
And finally, we call .map (mergeAll) on that array to get our final format.
Note that if you really have no other use for mergeAll, it's short and can easily be inlined in the main function as .map (xs => Object.assign ({}, ...xs)). It's a function that I use often and would simply have in handy in my utility toolbox, so I personally wouldn't inline it. You may feel differently.
Do notice the basic idea here. We don't try to solve the problem in one go, but rather apply a series of transforms to get to our final format. This allows us to take advantage of reusable functions for some of those steps. It's a powerful technique.
回答3:
Start building from empty array and add the 2 possibilities for key (true/false). Repeat the process by traversing all keys, for each key take the available results from previous.
let properties = ["arm", "lens", "season", "food", "size"];
const addTwo = (arr, key) => {
const result = [];
["true", "false"].forEach((val) =>
arr.forEach((item) => result.push({ ...item, [key]: val }))
);
return result;
};
const combinations = (arr) => {
let output = [{}];
arr.forEach((key) => (output = addTwo(output, key)));
return output;
};
console.log(combinations(properties));
来源:https://stackoverflow.com/questions/62114826/recursive-challenge-in-js-combining-all-possible-array-keys-in-true-false-vers