问题
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.length
objects 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.length
long: ("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