关于多维数组排列组合的问题

最近遇到了一个关于商品属性求组合的问题,怕我描述不好先上图:如下

图片描述

1、旁边的颜色尺码什么的 数量都是不确定的(因为有可能是家电,食品,或者服装什么的)
2、右边的属性值是允许手动添加的,所以数量也是不确定的

为了规范库存啥的,所以要分别排列组合出各种规格的商品并添加库存,我现在手动取到了上面的值格式如下:

data = {
    color:[value1,value2,value3,....],
    size:[value1,value2,value3,....],
    style:[value1,value2,value3,....]
}

然后我就不知道怎么求组合了,我主要是不知道如何递归,如何创建中间变量,求各位大佬给点思路,或者给出你们的代码让我参考一下,有注释更好,感谢!!!!深夜加班,我要哭了

阅读 9.6k
7 个回答

熬夜加班不容易呀,我搜了下资料,结合别人的例子,自己写了个
你可以参考一下。
主要思路是递归组合前两个

function handleCombine(arr) {
  debugger
  var len = arr.length;
  if (len >= 2) {
    //从前两个开始组合
    var len1 = arr[0].length;
    var len2 = arr[1].length;
    var lenBoth = len1 * len2;
    var items = new Array(lenBoth);
    var index = 0;
    //for循环构建两两组合后的数组
    for (var i = 0; i < len1; i++) {
      for (var j = 0; j < len2; j++) {
        if (arr[0][i] instanceof Array) {
          items[index] = arr[0][i].concat(arr[1][j]);
        } else {
          items[index] = [arr[0][i]].concat(arr[1][j]);
        }
        index++;
      }
    }
    // 新组合的数组取代原来前两个数组
    var newArr = new Array(len - 1);
    for (var i = 2; i < arr.length; i++) {
      newArr[i - 1] = arr[i];
    }
    newArr[0] = items;
    // 再次组合前两个
    return handleCombine(newArr);
  } else {
    return arr[0];
  }
}

var data = {
  color: ['color1', 'color2', 'color3'],
  size: ['size1', 'size2', 'size3'],
  style: ['style1', 'style2', 'style3']
}

var multiArr = []
for (key in data) {
  multiArr.push(data[key])
}

console.log(handleCombine(multiArr));

凑个热闹。。。

这个数据结构只有两层,只是每一层的数量不确定。所以两层循环就够了,没有必要递归。

var data = {
  color: ['color1', 'color2', 'color3'],
  size: ['size1', 'size2', 'size3'],
  style: ['style1', 'style2', 'style3']
}

Object.values(data).reduce( (result, property) => {
    return property.reduce( (acc, value) => {
        return acc.concat(result.map( ele => [].concat(ele, value)));
    }, []);
});

输出结果为包含对象的数组:

// 循环每一个商品属性
Object.keys(data).reduce( (result, key) => {

        // 循环属性的每一个值
        return data[key].reduce( (acc, value) => {

            // 对于第一个属性
            if (!result.length) {
                // 将数值转化为对象格式
                return acc.concat({ [key]: value });
            }

            // 对于第一个之后的属性,将新的属性和值添加到已有结果,并进行拼接。
            return acc.concat( result.map( ele => (Object.assign({}, ele, { [key]: value }) )));
        }, []);
    }, []);
let color = ['blue','red','white'];
let size = ['s','m','l'];
let style = [1,2,3];
let result = [];
for (var i = 0; i < color.length; i++) {
    for (var j = 0; j < size.length; j++) {
        for (var k = 0; k < style.length; k++) {
            result.push({
                color: color[i],
                size: size[j],
                style: style[k]
            })
        }        
    }    
}
console.log(result);

这个问题按照常规,全排列的前提是知道对象里面的所有属性,然后利用多种for循环或者简便的forEach语句进行一一配对

楼上已经把解决方案都列出来了,我这里说一下我的思考。

我在想能不能封装一个方法,能够自动遍历给定对象的所有属性所对应的值,然后将它们进行全排列。这样就不需要预先知道对象的所有key只要保证它的结构符合我们的要求即可。

这里要求的对象结构是:

var data = {
    key1: [val1, val2, ...],
    key2: [val1, val2, ..],
    key3: [val1, val2, ..],
    ...
};

经过实践,我封装出来了一个generatePropertyValueArrange(obj)方法,只要保证这个obj这个对象的结构符合我们的要求,就能够保证输出正确的所有对应属性值的数组的全排列组合。

思路如下:

  1. 先构造出对象的所有属性所对应值的数组遍历的forEach语句,类似以下的语句,即

       obj.key1.forEach(key1 => {
         obj.key2.forEach(key2 => {
         ...
         });
       });
  2. 然后使用eval来执行刚刚生成的forEach语句,这里有点像模板引擎的味道

完整代码如下:

// 构建符合要求的对象
var data = {
  color: ['red', 'blue', 'black'],
  size: ['S', 'M', 'L'],
  style: ['child', 'young', 'middle'],
  height: ['short', 'high'],
  // 这里可以添加形如key: [..., ...] 这种形式的项
};

// 调用构造方法
var dataArrange = generatePropertyValueArrange(data);

// 输出结果
console.log(dataArrange);

/*
* 生成对象的属性值的全排列
* @return array
* */
function generatePropertyValueArrange(obj) {
  var objKeys = Object.keys(obj);
  var length = objKeys.length;
  var statementChain = ''; // forEach的语句
  var num = 0; // 记录obj的key值遍历的位置,比如color为0,size为1
  var result = []; // 执行结果存放的数组

  getObjForEachStatement(); // 执行获取对象的forEach执行语句
  console.log(statementChain); // 打印以下执行语句
  eval(statementChain); // 执行生成的forEach语句

  // 返回result
  return result;

  /*
  * 具体生成执行语句的函数,主要通过修改statementChain,num和result
  * */
  function getObjForEachStatement() {
    var key = objKeys[num];

    // 比如生成:obj['color'].forEach( color => {
    statementChain += "obj['"+ key +"'].forEach( "+ key +" => {\n";

    // 如果已经是遍历到最后一个属性,那么push结果
    if (num === length - 1) {
      // 比如生成:color:color,size:size,style:style,height:height,
      // 属性名称:属性值的形式
      var item =  objKeys.reduce((acc, curr) => {
        return acc.concat([curr, ':', curr].join('')).concat(',');
      }, '');

      // 添加到forEach语句中
      // "});".repeat(length);的作用是封闭forEach语句
      statementChain += "result.push({\n\t"+ item +"\n});" + "});".repeat(length);
    }

    num++;

    if (num < length) {
      // 属性还没有遍历完,递归
      getObjForEachStatement();
    }
  }
}

测试结果如下:

// 生成的forEach语句
obj['color'].forEach( color => {
obj['size'].forEach( size => {
obj['style'].forEach( style => {
obj['height'].forEach( height => {
result.push({
    color:color,size:size,style:style,height:height,
});});});});});

// 构造的全排列数组
[ { color: 'red', size: 'S', style: 'child', height: 'short' },
  { color: 'red', size: 'S', style: 'child', height: 'high' },
  { color: 'red', size: 'S', style: 'young', height: 'short' },
  { color: 'red', size: 'S', style: 'young', height: 'high' },
  { color: 'red', size: 'S', style: 'middle', height: 'short' },
  { color: 'red', size: 'S', style: 'middle', height: 'high' },
  { color: 'red', size: 'M', style: 'child', height: 'short' },
  { color: 'red', size: 'M', style: 'child', height: 'high' },
  { color: 'red', size: 'M', style: 'young', height: 'short' },
  { color: 'red', size: 'M', style: 'young', height: 'high' },
  { color: 'red', size: 'M', style: 'middle', height: 'short' },
  { color: 'red', size: 'M', style: 'middle', height: 'high' },
  { color: 'red', size: 'L', style: 'child', height: 'short' },
  ......

大概就这些。

总结了几位大神的答案,我自己提出了一个新的需求,我想最终的数据格式是一个成员为对象的数组,对象包含的属性就是我要使用的值,所以我消化了一下撸出了下面这段:

        var data = {
            size: ['size1', 'size2', 'size3'],
            color: ['color1', 'color2', 'color3'],
            style: ['style1', 'style2', 'style3']
        };

        var pr = (function(data){
            var aa = [];
            for(var pro in data){
                aa.push(pro);
            }
            return aa;
        })(data);
        Object.values(data).reduce(function(objs,props,index){
            debugger
            if(objs.length==0){
                return props.reduce(function(oo,att){
                    var oob = new Object();
                    oob[pr[index]] = att;
                    return oo.concat(oob);
                },[]);
            }else{
                var item = objs.reduce(function(ritem,oo){
                    var tt = props.reduce(function(subitem,att){
                        var oob = new Object();
                        for(var i in oo){
                            oob[i] = oo[i];
                        }
                        oob[pr[index]] = att;
                        subitem.push(oob);
                        return subitem;
                    },[]);
                    return ritem.concat(tt)
                },[]);
                return item;
            }
            
        },[])

关注一下~
楼上给出的都是固定的3个数组的情况下,如果数组数量并不固定是无法解决的

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏