从电商sku的全排列到笛卡尔积

2,575 阅读2分钟

故事的起源都从我看到这么一个问题开始:

let names = ["iPhone X", "iPhone XS"]

let colors = ["黑色", "白色"]

let storages = ["64g", "256g"]

有这么几个数组,列出他们的所有组合,最终出来的结果像这样:

[
  ["iPhone X", "黑色", "64g"],
  ["iPhone X", "黑色", "256g"],
  ["iPhone X", "白色", "64g"],
  ["iPhone X", "白色", "256g"],
  ["iPhone XS", "黑色", "64g"],
  ["iPhone XS", "黑色", "256g"],
  ["iPhone XS", "白色", "64g"],
  ["iPhone XS", "白色", "256g"],
]

这个问题的难点在于上面的数组个数是可以增删的,所以不能粗暴地根据上面3个数组就直接3重循环来解决。
大家可以先在这里暂停一下,自己思考下解决方案。


解决方案

我自己花了半个小时把代码写出来了,但是发现没大佬写的那么优雅,所以这里借用一下大佬的解决方案放上来给大家参考。

let names = ["iPhone X", "iPhone XS"]

let colors = ["黑色", "白色"]

let storages = ["64g", "256g"]

let combine = function (...chunks) {
  let res = []

  let helper = function (chunkIndex, prev) {
    let chunk = chunks[chunkIndex]
    let isLast = chunkIndex === chunks.length - 1
    for (let val of chunk) {
      let cur = prev.concat(val)
      if (isLast) {
        // 如果已经处理到数组的最后一项了 则把拼接的结果放入返回值中
        res.push(cur)
      } else {
        helper(chunkIndex + 1, cur)
      }
    }
  }

  // 从属性数组下标为 0 开始处理
  // 并且此时的 prev 是个空数组
  helper(0, [])

  return res
}

console.log(combine(names, colors, storages))

作者:晨曦时梦见兮
链接:https://juejin.im/post/5ee6d9026fb9a047e60815f1
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

大家有兴趣的话也可以看下「前端电商 sku 的全排列算法很难吗?学会这个套路,彻底掌握排列组合」这篇文章,里面也有作者的解决思路和扩展知识分享。

扩展

查阅了其他资料之后,发现这类题目属于笛卡尔积问题,**画重点,笛卡尔积问题~****,**在stackoverflow上还有一个大佬的极简解法,这里也放上来给大家欣赏。

const f = (a, b) => [].concat(...a.map(d => b.map(e => [].concat(d, e))));
const cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a);
let output = cartesian([1,2],[10,20],[100,200,300]);

输出output

[ [ 1, 10, 100 ],
  [ 1, 10, 200 ],
  [ 1, 10, 300 ],
  [ 1, 20, 100 ],
  [ 1, 20, 200 ],
  [ 1, 20, 300 ],
  [ 2, 10, 100 ],
  [ 2, 10, 200 ],
  [ 2, 10, 300 ],
  [ 2, 20, 100 ],
  [ 2, 20, 200 ],
  [ 2, 20, 300 ] ]

你没看错,就两行代码解决了,大佬请收下我膝盖,真牛B。文章在这里:Cartesian product of multiple arrays in JavaScript,感兴趣可以看看。