前端电商 sku 的全排列算法
- 什么是sku
- 聊聊常见的需求
- 解决思路
- 思路分解
- 上代码
什么是sku
针对电商而言:
1、SKU是指一款商品,每款都有出现一个SKU,便于电商品牌识别商品。
2、一款商品多色,则是有多个SKU,例:一件衣服,有红色、白色、蓝色,则SKU编码也不相同,如相同则会出现混淆,发错货。
SKU,定义为保存库存控制的最小可用单位,例如纺织品中一个SKU通常表示:规格、颜色、款式。 STOCK KEEP UNIT,这是客户拿到商品放到仓库后给商品编号,归类的一种方法。
聊聊常见的需求
我们有三个数组分别为
let names = ["星途 XT", "星途 XTL"]
let colors = ["黑色", "白色", "金色"]
let displacement = ["1.5L", "1.5T", "1.8L", "1.8T"]
最终输出结果为
[
["星途 XT","黑色","1.5L"],
["星途 XT","黑色","1.5T"],
["星途 XT","黑色","1.8L"],
["星途 XT","黑色","1.8T"],
["星途 XT","白色","1.5L"],
["星途 XT","白色","1.5T"],
["星途 XT","白色","1.8L"],
["星途 XT","白色","1.8T"],
["星途 XT","金色","1.5L"],
["星途 XT","金色","1.5T"],
["星途 XT","金色","1.8L"],
["星途 XT","金色","1.8T"],
["星途 XTL","黑色","1.5L"],
["星途 XTL","黑色","1.5T"],
["星途 XTL","黑色","1.8L"],
["星途 XTL","黑色","1.8T"],
["星途 XTL","白色","1.5L"],
["星途 XTL","白色","1.5T"],
["星途 XTL","白色","1.8L"],
["星途 XTL","白色","1.8T"],
["星途 XTL","金色","1.5L"],
["星途 XTL","金色","1.5T"],
["星途 XTL","金色","1.8L"],
["星途 XTL","金色","1.8T"]
]
由于这些属性数组是不定项的,所以不能使用简单的三重暴力循环来解决这个需求了
解决思路
我们采取递归回溯法来解决这个问题,那么最重要的问题就是设计我们的递归函数
思路分解
以上文所举的例子来说,比如我们目前的属性数组就是:names、colors、displacement,首先我们会处理 names 数组,很显然对于每个属性数组,都需要去遍历它,然后一个一个选择后再去和 下一个数组的每一项进行组合。
我们设计的递归函数接受两个参数:
index 对应当前正在处理的下标,是 names 还是 colors 或是 displacement。
prev 上一次递归已经拼接成的结果,比如 ['星途 XT, ‘黑色’]。
进入递归函数:
- 处理属性数组下标为0: 假设在第一次 星途 XT, 那此时我们有一个未完成的结果状态,假设我们叫它prev, 此时prev = [“星途 XT”].
- 处理属性数组的下标1: 那么就处理到 colors 数组的了,并且我们拥有 prev,在遍历 colors 的时候继续递归的去把 prev 拼接成 prev.concat(color),也就是 [‘星途 XT’, ‘黑色’] 这样继续把这个 prev 交给下一次递归。
- 处理属性数组的下标2:那么就处理到 displacement 数组的了,并且我们拥有了 name + color 的 prev,在遍历 displacement 的时候继续递归的去把 prev 拼接成 prev.concat(displacement),也就是 [‘星途 XT’, ‘黑色’, ‘1.5L’],并且此时我们发现处理的属性数组下标已经到达了末尾,那么就放入全局的结果变量 res 中,作为一个结果。
上代码
let names = ["星途 XT", "星途 XTL"]
let colors = ["黑色", "白色", "金色"]
let displacement = ["1.5L", "1.5T", "1.8L", "1.8T"]
let combine = function (...chunks) {
console.log(chunks, '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, displacement))
第一次进入递归流程。剩余流程可脑补