// 需要解决的问题
例1
let sizes = ['x', 'xl'];
let colors =['黑', '白', '灰'];
let czs = ['棉', '涤纶'];
let heights = ['170cm', '180cm'];
let arr = [];
for(let size of sizes) {
for(let color of colors) {
for(let cz of czs) f
for(let height of heights) {
arr.push({item: `${size}-${color}-${cz}-${height}`});
}
}
}
}
// 例2 怎么实现像上面那样的效果
let specs = [
['x', 'xl'],
['黑', '白', '灰'],
['棉', '涤纶'],
['170cm', '180cm'],
]
答案是给了,还是讲下思路吧。
简单的说就是遍历数组,每组数据与之前的结果“相乘”(笛卡尔积)。比如
["x", "xl"]
与["黑", "白", "灰"]
的乘积是:这里如果我们把
["x", "xl"]
看成是[["x"], ["xl"]]
就会发现,被乘集和结果集是一样的结构,都是二维数组,每次乘一个新的集合,只是把被乘集拷贝 n(n 是新数组长度)份,每份依次加入新集合的元素,最后把结果展平(合并)。比如对上面的集合再乘个
["170cm", "180cm"]
,① 先把原结果集元素拷贝成 2 份(新数组长度)
再依次加入新元素,第 ❶ 份加
170cm
,第 ❷ 份加180cm
最后 flat 一层(合并两个二维数组)得到
依次循环下去,就能拿到最后的结果。当然最后结果的每个元素都是一个包含了各项规格的数组。再用
join
把元素连接起来成一个字符串就是最终结果,这步比较简单,算后期处理了。问题是,既然是循环,那循环体就应该有一致性。简单说就是每次处理的输入输出是一致的(递归也是这个思想)。在乘第一个数组,就是
["x", "xl"]
那个的时候,没有被乘集,我们应该初始化一个出来。这个集合肯定不是能空的(从数学的思想来看, 0 乘以任何数都是 0),所以这个初始集合应该是[[]]
。这个集合有一个元素,这个元素是个空集合(因为后面是往里 + 规格,不再是乘,所以可空)整个过程用循环来写就是
这个循环可以改写成
reduce
;拷贝过程可以用map
代替,flat
和map
可以合并成flatMap
,再把最后的处理过程连接上去,就得到了上面的答案。再补充两个递归的写法:
1) 是把结果集作为输出参数传入递归(封装了 IIFE,所以外部接口不需要准备输出参数)
2) 每轮直接返回结果
由于每轮输入集合数量与输出数量不等(是展开关系),不能直接用 map,需要使用 flatMap
这种方式通过可选参数能提供简化后的调用接口。但是如果想限制用户不乱输入后面两个参数,最好还是再用 IIFE 封一下。