给定一个数组
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));