Let's decompose the problem. First, let's get extracting the first element of each subarray out of the way:
function get_elts(data, idx) {
return data.map(function(v) { return v[idx]; });
}
So
> get_elts(data, 0) // ['A', 'B', 'C']
Decomposing the problem like this is fundamental to good program design. We don't want to write things which jumble up multiple problems. In this case, the multiple problems are (1) getting the first element of each subarray and (2) finding the combinations. If we write one routine which mixes up the two problems, then we will never be able to re-use it for other things. If our boss comes and says now he wants to find all the combinations of the second element of each subarray, we'll have to cut and paste and create nearly duplicate code. Then we'll be maintaining that code for the rest of our lives or at least until we quit. The rule about factoring is do it sooner rather than later.
Then, create all combinations of any two arrays:
function combinations(arr1, arr2) { //create all combos of elts in 2 arrays by
return [].concat.apply( //concatenating and flattening
[], //(starting with an empty array)
arr1.map( //a list created from arr1
function(v1) { //by taking each elt and from it
return arr2.map( //creating a list from arr2
function(v2) { //by taking each element and from it
return [v1, v2]; //making a pair with the first elt
}
);
};
)
);
}
Normally we would write this more compactly. Let's walk through it:
Array#concat combines one or more things, or elements inside those things if they are arrays, into an array.
Function#apply lets us provide an array that will turn into the argument list of concat.
Array#map creates a parallel array to arr1, which contains...
- elements which are two-element arrays based on looping over
arr2.
Right, this is not your mother's JavaScript. It's almost a different language from the style where you initialize this and set that and loop over the other thing and return something else. By adopting this style, we end up with code which is more precise, concise, reusable, provably correct, future-friendly, and maybe optimizable.
By future-friendly, I mean among other things ES6-friendly. The above could be rewritten as:
combinations = (arr1, arr2) => [].concat(...arr1.map(v1 => arr2.map(v2 => [v1, v2])));
Get ready guys and girls, this will come up in your job interviews pretty soon now. Time to move on from jQuery.
Now the problem can be expressed as:
var first_elts = get_elts(data, 0);
combinations(first_elts, first_elts);