让我们分解问题。首先,让我们提取每个子数组的第一个元素:
function get_elts(data, idx) {
return data.map(function(v) { return v[idx]; });
}
所以
> get_elts(data, 0) // ['A', 'B', 'C']
像这样分解问题是良好程序设计的基础。我们不想写混杂多个问题的东西。在这种情况下,多个问题是(1)获取每个子数组的第一个元素和(2)找到组合。如果我们编写一个将这两个问题混为一谈的例程,那么我们将永远无法将其重新用于其他事情。如果我们的老板现在说他想找到每个子数组的 second 元素的所有组合,我们将不得不剪切和粘贴并创建几乎重复的代码。然后我们将在我们的余生或至少在我们退出之前维护该代码。关于因式分解的规则是越早越好。
然后,创建任意两个数组的所有组合:
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
}
);
};
)
);
}
通常我们会写得更紧凑。让我们来看看:
-
Array#concat 将一个或多个事物或这些事物中的元素(如果它们是数组)组合成一个数组。
-
Function#apply 让我们提供一个数组,该数组将变成concat 的参数列表。
-
Array#map 为arr1 创建一个并行数组,其中包含...
- 元素是基于循环
arr2 的二元素数组。
对,这不是你妈妈的 JavaScript。它与您初始化 this 并设置 that 并循环另一件事并返回其他内容的风格几乎是一种不同的语言。通过采用这种风格,我们最终得到的代码更加精确、简洁、可重用、可证明正确、面向未来并且可能可优化。
对于未来友好,我的意思是对 ES6 友好。以上可以改写为:
combinations = (arr1, arr2) => [].concat(...arr1.map(v1 => arr2.map(v2 => [v1, v2])));
准备好男人和女孩,这很快就会出现在你的工作面试中。是时候从 jQuery 继续前进了。
现在问题可以表示为:
var first_elts = get_elts(data, 0);
combinations(first_elts, first_elts);