patternjavascriptMinor
Calculate all possible combinations of an array of arrays or strings
Viewed 0 times
arrayscombinationsarrayallpossiblecalculatestrings
Problem
I'm using code adapted from this Stack Overflow question to calculate all possible combinations from an array, which may contains strings.
For example:
However as the items could be strings or array I need to check for all possible combinations of array/string pairs when building the results.
This has ended in rather messy code, and I'd appreciate any hints on how to tidy it up. I've attempted to refactor it, but I guess I'm tired and missing something!
`function allPossibleCases(arr) {
if (arr.length === 0) {
return [];
}
else if (arr.length === 1){
return arr[0];
}
else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1)); // recur with the rest of array
if(typeof allCasesOfRest === 'string') {
if(typeof arr[0] === 'string') {
result.push(arr[0] + " " + allCasesOfRest);
} else {
for (var i = 0; i
For example:
[ "x", "y", "z" ] => "x y z"
[ [ "a", "b"] , "c", "d" ] => [ "a c d", "b c d" ]
[ [ "a", "b"] , ["c" "d"], "e"] => [ "a c e", "b c e", "a d e", "b d e" ]
However as the items could be strings or array I need to check for all possible combinations of array/string pairs when building the results.
This has ended in rather messy code, and I'd appreciate any hints on how to tidy it up. I've attempted to refactor it, but I guess I'm tired and missing something!
`function allPossibleCases(arr) {
if (arr.length === 0) {
return [];
}
else if (arr.length === 1){
return arr[0];
}
else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1)); // recur with the rest of array
if(typeof allCasesOfRest === 'string') {
if(typeof arr[0] === 'string') {
result.push(arr[0] + " " + allCasesOfRest);
} else {
for (var i = 0; i
Solution
-
You don't need the
-
As you yourself noted, normalize the input, so you don't need to be type-checking inside the loop
But I'd advice you ignore that you're dealing with strings specifically, and just worry about "combinations of stuff", and try for a generic solution.
Still, the manual array-building makes your code cryptic. I'd suggest a more functional approach, using
Not saying it's easier to follow at first glance, but there's less code, and it's generic:
To convert the output of the above function to strings, just add another
You get this:
Note that the order of the last one is different from yours, though it contains the same combinations.
You don't need the
else if/else blocks at the top; it can just be if blocks, since they return and thus exit the function anyway.if (arr.length === 0) {
return []; // stop here
}
if (arr.length === 1){
return arr[0]; // or here
}
// or continue with the rest of the function-
As you yourself noted, normalize the input, so you don't need to be type-checking inside the loop
But I'd advice you ignore that you're dealing with strings specifically, and just worry about "combinations of stuff", and try for a generic solution.
Still, the manual array-building makes your code cryptic. I'd suggest a more functional approach, using
map and reduce.Not saying it's easier to follow at first glance, but there's less code, and it's generic:
// this produces a 2d array of combinations
function combinations(array) {
if(!array.length) {
return [];
}
// wrap non-array values
// e.g. ['x',['y','z']] becomes [['x'],['y','z']]
array = array.map(function (item) {
return item instanceof Array ? item : [item];
});
// internal recursive function
function combine(list) {
var prefixes, combinations;
if(list.length === 1) {
return list[0];
}
prefixes = list[0];
combinations = combine(list.slice(1)); // recurse
// produce a flat list of each of the current
// set of values prepended to each combination
// of the remaining sets.
return prefixes.reduce(function (memo, prefix) {
return memo.concat(combinations.map(function (combination) {
return [prefix].concat(combination);
}));
}, []);
}
return combine(array);
}To convert the output of the above function to strings, just add another
map call:function allPossibleCases(arr) {
return combinations(arr).map(function (combination) {
return combination.join(" ");
});
}You get this:
[ 'x', 'y', 'z' ] => [ 'x y z' ]
[ [ 'a', 'b' ], 'c', 'd' ] => [ 'a c d', 'b c d' ]
[ [ 'a', 'b' ], [ 'c', 'd' ], 'e' ] => [ 'a c e', 'a d e', 'b c e', 'b d e' ]Note that the order of the last one is different from yours, though it contains the same combinations.
Code Snippets
if (arr.length === 0) {
return []; // stop here
}
if (arr.length === 1){
return arr[0]; // or here
}
// or continue with the rest of the function// this produces a 2d array of combinations
function combinations(array) {
if(!array.length) {
return [];
}
// wrap non-array values
// e.g. ['x',['y','z']] becomes [['x'],['y','z']]
array = array.map(function (item) {
return item instanceof Array ? item : [item];
});
// internal recursive function
function combine(list) {
var prefixes, combinations;
if(list.length === 1) {
return list[0];
}
prefixes = list[0];
combinations = combine(list.slice(1)); // recurse
// produce a flat list of each of the current
// set of values prepended to each combination
// of the remaining sets.
return prefixes.reduce(function (memo, prefix) {
return memo.concat(combinations.map(function (combination) {
return [prefix].concat(combination);
}));
}, []);
}
return combine(array);
}function allPossibleCases(arr) {
return combinations(arr).map(function (combination) {
return combination.join(" ");
});
}[ 'x', 'y', 'z' ] => [ 'x y z' ]
[ [ 'a', 'b' ], 'c', 'd' ] => [ 'a c d', 'b c d' ]
[ [ 'a', 'b' ], [ 'c', 'd' ], 'e' ] => [ 'a c e', 'a d e', 'b c e', 'b d e' ]Context
StackExchange Code Review Q#52119, answer score: 7
Revisions (0)
No revisions yet.