给定一个数组
let data = [{name: "颜色",specs: ["白色", "黑色"],},{name: "尺寸",specs: ["14寸","15寸", "16寸"],},{name: "处理器",specs: ["i5", "i7", "i9"],},];
组合处理(据说这套路叫什么啥笛卡尔积)
方法很巧妙,自己写不一定写的出,但是可以借鉴前人的经验,然后去试着理解,分析,即使写不出也要能看的懂,如果完成任务直接拿现成的方法用也无妨
function combine(arr) {let result = [[]];//定义一个数组必须要有一项(且这一项必须是数组)arr.map((x) => {let res = [];console.log(result,'result');result.map((y) => {x.specs.map((z) => {//最内层循环x.specs.map第一次执行完之后res的值就是[["白色"], ["黑色"]],因为specs的值为两项["白色", "黑色"]循环两遍,循环完后跳出循环到上一层循环,//此时上一层的循环result.map的result还是[[]],所以执行完一遍之后跳出当前循环到上一层循环,//此时arr.map循环完第一遍开始赋值将result = res,此时result为[["白色"], ["黑色"]],然后开始进入result.map循环//此时x.specs.map的值为arr.map的第二次循环也就是循环["14寸","15寸", "16寸"],而result.map为[["白色"], ["黑色"]],//此时循环的步骤为result.map第一遍循环result,此时y为["白色"],而z为arr第二项的specs里面的每一项,于是用["白色"]去拼接["14寸","15寸", "16寸"]的每一项//此时就res就变成了result.map[["白色"], ["黑色"]]跟["14寸","15寸", "16寸"]去按顺序一项一项的去组合,组合完之后将 result = res,//以此类推便出现了最终的所有值的组合结果,很巧妙的一个套路,自己去从0开始写还是比较费脑筋的,尤其逻辑处理能力尚不强的时候以及没多少经验的时候res.push([...y, z]);});});result = res;//将第一次循环之后的值赋值给result,此时result为[["白色"], ["黑色"]],故下一波循环得循环两次});return result;}console.log(combine(data));
逻辑解释(给自己看的),配合打印的值看更容易理解
//最内层循环x.specs.map第一次执行完之后res的值就是[["白色"], ["黑色"]],因为specs的值为两项["白色", "黑色"]循环两遍,循环完后跳出循环到上一层循环,
//此时上一层的循环result.map的result还是[[]],所以执行完一遍之后跳出当前循环到上一层循环,
//此时arr.map循环完第一遍开始赋值将result = res,此时result为[["白色"], ["黑色"]],然后开始进入result.map循环
//此时x.specs.map的值为arr.map的第二次循环也就是循环["14寸","15寸", "16寸"],而result.map为[["白色"], ["黑色"]],
//此时循环的步骤为result.map第一遍循环result,此时y为["白色"],而z为arr第二项的specs里面的每一项,于是用["白色"]去拼接["14寸","15寸", "16寸"]的每一项
//此时就res就变成了result.map[["白色"], ["黑色"]]跟["14寸","15寸", "16寸"]去按顺序一项一项的去组合,组合完之后将 result = res,
//以此类推便出现了最终的所有值的组合结果,很巧妙的一个套路,自己去从0开始写还是比较费脑筋的,尤其逻辑处理能力尚不强的时候以及没多少经验的时候
方法二:递归调用
这个方法稍微理解起来有点绕
const generateCombinations = (arr, result = [], current = {}, i = 0)=>{// 由于arr.length的长度一般都不会等于,所以最开始直接执行else// 第二次函数调用i等于1,而数组长度依旧为3,所以还是执行else// 第三次函数调用,此时i等于2,数组长度依旧为3,所以还是执行else// 第四次函数调用,此时i等于3,i已经等于数组长度了,故执行if,此时current为,i5 {颜色: '白色', 尺寸: '14寸', 处理器: 'i5'},// 当第四次函数调用完之后执行return result,但是函数之前的循环并没有终止掉,上一步的循环停留在调用递归的地方// 而此时函数执行到处理器规格的specs循环的第二遍,此时current等于i7 {颜色: '白色', 尺寸: '14寸', 处理器: 'i7'},i还是之前的没有加一的i所以为2// 于是再次执行又要递归i再次加1等于3// 后面就不解释了,直接看打印值,很巧妙的一种方法,理解起来稍微有点绕不如笛卡尔积写法if (i === arr.length) {console.log('执行push');result.push({...current});} else {for (const value of arr[i].specs) {// 第一次循环(此处第一次循环为颜色规格的specs第一次循环)current[arr[i].name]的arr[i].name值为:'颜色',而value为颜色规格的每一项,// 此时循环第一次之后arr不变,result不变,current为 白色 {颜色: '白色'},i+1等于1,然后执行递归// 第二次循环,由于执行了递归,第二次循环变成了循环尺寸规格的specs,// 此时current为 14寸 {颜色: '白色', 尺寸: '14寸'},i+1等于2,然后再次递归,// 第三次循环,由于执行了递归,第三次循环变成了循环处理器规格的specs,// 此时current为 i5 {颜色: '白色', 尺寸: '14寸', 处理器: 'i5'},i+1等于3,然后再次递归,current[arr[i].name] = value;console.log(arr[i].name,'----',value,current,i);generateCombinations(arr, result, current, i + 1);}}return result;}console.log(generateCombinations(data));