So I have an array which looks like this:
['id1/id2', 'id3', 'id4/id5/id6']
Although it may have more than 3 entries, or more split values (/ things) - these may change depending on the state of the app but this is a standard example.
I am trying to output:
[
['id1', 'id3', 'id4'],
['id1', 'id3', 'id5'],
['id1', 'id3', 'id6'],
['id2', 'id3', 'id4'],
['id2', 'id3', 'id5'],
['id2', 'id3', 'id6']
]
I think a forEach loop can be used to recurse through but I got stuck on the syntax, and so tried a standard functional recursive tpye. I also was unsure about the return procedure to generate the output. What I have so far is:
singleOptionStemGenerator(route: string[]): [string[]] {
let returnArray: [string[]];
route.forEach((options: string) => {
// split into the various options
let splitOption: string[] = options.split('/');
splitOption.forEach((str: string) => {
if(route.length > 1) {
this.singleOptionStemGenerator(route.splice(0, 1));
} else {
return str;
}
})
});
return returnArray;
}
But I am not sure how to amalgamate the ID values into a new array, and then add that to a new array overall.
EDIT: The solution given below for myself was a little dense, so I went through and made it a function and fully annotated it. I also made it typescript (which I use). The solution is all what the answer this, this just feel sanitzed for easier reading and learning: https://codepen.io/bogomip/pen/LYLjrxa
CodePudding user response:
What you're looking for is usually called the Cartesian Product. With a helper function that computes those, this becomes almost trivial.
Here is one version:
const cartesian = ([xs, ...xss]) =>
xs = undefined
? []
: xss.length == 0
? xs .map (x => [x])
: xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))
const separate = (xs) =>
cartesian (xs .map (x => x .split ('/')))
console .log (separate (['id1/id2', 'id3', 'id4/id5/id6']))
.as-console-wrapper {max-height: 100% !important; top: 0}
Update
A comment made it clear that this code is somewhat dense. Part of that is my penchant for expression-only coding. For those more used to expression-and-statement-style Javascript, this version might be more familiar:
const cartesian = (xss) => {
if (xss .length == 0) {
return []
}
const first = xss [0]
const rest = xss .slice (1)
if (rest .length == 0) {
return first .map (x => [x])
}
const cartesianEnd = cartesian (rest)
return first .flatMap (
x => cartesianEnd .map (ys => [x, ...ys])
)
}
If that style is more to your liking, then that may also help explain the code as I wrote it. Except that... typing that out made me realize that there was a horrible inefficiency in my first approach. Unfortunately the fix adds some complexity, but it is clearly worthwhile:
const cartesian = ([xs, ...xss]) =>
xs = undefined
? []
: xss.length == 0
? xs .map (x => [x])
: ((yss = cartesian (xss)) => xs .flatMap (x => yss .map (ys => [x, ...ys]))) ()
The problem was that I was recalculating the product of the remaining arrays for each element of my current array. These don't change, and we should do this only once. Ahh for real let
-bindings in JS! We could do this in many different manners, but this one is not too bad.