数组相乘,顾名思义就是将多个数组的每一元素乘(组合)起来。它的结果以几何数级增长,初次遇到此类问题时,常常使人头皮发麻,我们现在以js的角度来解决这个问题。
从实例出发
众所周知,女孩出门前一般需要经过精心打扮,那么假设你有一个女朋友,她有着3顶帽子,5件衣服,5条裤子,3双鞋子,2只口红,4个包包,2副墨镜,且心情可能会影响穿着,她可能因为心情不好而选择不带一些物品,但是她会告诉你她会穿戴什么,要求列举所有方案,看到题目后……
不说了,先将实际问题转化成语言问题吧。 七个数组,分别表示七种穿戴,数组中存值为该穿戴的代号,传值为字符串是她告诉你她需要的穿着,例"clothes trousers",中间以空格隔开const hat = ['a','b','c'];const clothes = ['0','1','2','3','4'];const trousers = ['q','w','e','r','t'];const shoes = ['A','B','C'];const lipstick = ['$1','$2'];const bag = ['¥1','¥2','¥3','¥4'];const sunglasses = ['^1','^2'];function getComb(str){ return arr;}复制代码
解决思路
秉着不管什么问题,看到数组我就用循环的思路,去解题,难免会遇上很多问题。如果,题目上明确指出女朋友心情必须max每次出门都装备拉满,那ok没问题,7次循环解决。但是女人心海底针呐,你只能通过她告诉的穿着来列举(如果无论什么情况,你都用7次循环,那当我没说)。
随机数法
我们可以让电脑自己随机组合,并判断如果已经添加过这个结果,就不加进数组就ok了。
const arr = str.split(' ') .map(name => { switch (name) { case 'hat': return hat; case 'clothes': return clothes; case 'trousers': return trousers; case 'shoes': return shoes; case 'lipstick': return lipstick; case 'bag': return bag; case 'sunglasses': return sunglasses; } })复制代码
先将传入的字符串转化一个与它传值相关的二维数组,例:传入"hat clothes"
arr为[ [ 'a', 'b', 'c' ], [ '0', '1', '2', '3', '4' ] ],这样我们就得知了穿戴的数目,以及该穿戴的种类了。我们不难得出,这种情况下最多能列出15种结果,定义一个total变量并修改一下上面代码。let total = 1;const arr = str.split(' ') .map(name => { switch (name) { case 'hat': total *= hat.length ; return hat; case 'clothes': total *= clothes.length ; return clothes; case 'trousers': total *= trousers.length ; return trousers; case 'shoes': total *= shoes.length ; return shoes; case 'lipstick': total *= lipstick.length ; return lipstick; case 'bag': total *= bag.length ; return bag; case 'sunglasses': total *= sunglasses.length ; return sunglasses; } })复制代码
如果使用随机数我们就能将一个多次循环转化为一个while判断的for循环。我们new一个数组result,由于我们知道一共有几种结果,所以while循环的条件就是:result.length < total.
let result = [], sum = ''; while (result.length < total) { for (let i = 0; i < arr.length; i++) { sum += arr[i][parseInt(Math.random() * arr[i].length)]; } if (result.indexOf(sum) == -1) result.push(sum); sum = ''; } return result;复制代码
注意:Math.random()是产生一个[0,1)的随机浮点数,数组的索引是整数,所以我们需要转换类型;sum是数组的组合,所以我们在添加完后需要了给它还原.
随机数法的缺点:
算法过于暴力,由于Math.random值返回的不确定性,导致很多时间会浪费在生成一个数组已经添加过的值上。reduce方法
我们也可以使用reduce方法非常简便的完成数组的乘法.很多初学者可能会对reduce比较陌生,因为这是一个不常见的方法,但是使用reduce进行很方便,先简单介绍一下reduce方法。
reduce方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终为一个值
reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
arr.reduce(callback,[initialValue])callback (执行数组中每个值的函数,包含四个参数)previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))currentValue (数组中当前被处理的元素)index (当前元素在数组中的索引)array (调用 reduce 的数组)initialValue (作为第一次调用 callback 的第一个参数。)复制代码
基础用法
const a = [1,2,3,4,5,6]; const rst = a.reduce((pre,cur)=>{ return pre + cur; }) console.log(rst); // rst = 1+2+3+4+5+6复制代码
pre为第一个值或上次计算的结果,这里没有传初值,pre初始默认为0,cur为数组的每一个值。这里解析过程:
=> pre = 0 ; cur = a[0];=> pre = 0 + a[0] ; cur = a[1];=> pre = 0 + a[0] + a[1] ; cur = a[2];...rst = 0 + 1 + 2 + 3 + 4 + 5 + 6 // 21复制代码
用reduce解决数组相乘
return arr.reduce((pre,cur)=>{ return [].concat(...pre.map(e=>cur.map(ele=>ele+e))) }) 复制代码
例:
str = 'clothes hat bag';=> arr :[ [ '0', '1', '2', '3', '4' ], [ 'a', 'b', 'c' ], [ '¥1', '¥2', '¥3', '¥4' ] ]复制代码
首先将一个复杂组合问题,转化为叠加组合问题。如果能将arr[0],arr[1]两个组合的结果返回给pre,那么我们就能通过两两组合来完成复杂组合。接下来使用map完成两两组合:
pre : [ '0', '1', '2', '3', '4' ]cur : [ 'a', 'b', 'c' ]pre.map(e => cur.map(ele => e + ele));/*第一次*/e : '0' , ele: 'a' , e + ele ='0a', pre[['0a']]e : '0' , ele: 'b' , e + ele = '0b', pre[['0a','0b']].../*结束*/pre[['0a','0b','0c'],['1a','1b','1c'],['2a','2b','2c'],['3a','3b','3c'],['4a','4b','4c' ]]cur : [ '¥1', '¥2', '¥3', '¥4' ]复制代码
接下来我们只需要将二维数组pre转化为一维数组,然后函数就会随着reduce一步一步将数组相乘起来了
[].concat[...pre.map(e => cur.map(ele => e + ele))]复制代码
扩展运算符( spread )是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,一般在传参时使用。
到这里我们就解决这个女朋友出门的问题了。