什么是SKU
sku就是我们在京东或者拼多多购物的时候,选择的商品的属性。
通过动态图很容易明白什么是sku是什么,就是选择一件商品的不同属性。 从图中我们可以看到可选的sku有男裤黑色L、男裤白色L、女裤白色S、女裤白色L.
SKU中的概念
类型 :
类型就是上图中可选择的每一行数据,比如上图中就包含三个类型
[
// 第一种类型
[男裤,女裤],
// 第二种类型
[黑色,白色],
// 第三种类型
[S,L],
]
规格:
规格就是上图中的每个可以选择的属性,比如上图中的类型的综合,也就是
[男裤,女裤,黑色,白色,S,L]
可选择的SKU:
因为不是每件商品的规格都可以选择,比如上图中男裤白色S,就是不可选的sku,可选的sku有4种,如下:
SKU 算法难点分析
SKU 算法难点是在哪里呢?
我们可以想一下,如果是我们实现,我们会如何实现该功能呢?
难就难在当我们选择某个规格以后,如何确定还有哪些规格是可以选择的,比如选择了规格S以后,男裤和黑色就需要置灰,不能选择了,因为目前男裤黑色S的SKU是没有的。
所以难点就是当点击某个规格以后,所有的规格都需要重新计算一遍,以便确定规格是否可点或者disabled掉。
通过上面的分析,问题就转换为当选择了某个规格后,如何通过可选的SKU查找出哪些规格还可以点击或者哪些规格需要被disabled 掉。
SKU质数算法
- SKU质数算法使用的数据例子
// type: 规格类型,每个规格是指type中的一个值,比如男裤,女裤,黑色,白色等
type: [
["男裤", "女裤"],
["黑色", "白色"],
["S", "L"],
],
// 可选的SKU
const canUseSku = [
{
// 当前SKU的可选数量
stock: 1,
// 当前SKU包含的规格
skuName: ["男裤", "黑色", "L"],
// 当前SKU包含的规格对应的质数
skuPrime: [2, 5, 13],
},
{
stock: 1,
skuName: ["男裤", "白色", "L"],
skuPrime: [2, 7, 13],
},
{
stock: 1,
skuName: ["女裤", "白色", "S"],
skuPrime: [3, 7, 11],
},
{
stock: 1,
skuName: ["女裤", "白色", "L"],
skuPrime: [3, 7, 13],
},
];
- 为什么选择质数
因为质数只有1和自身,比如有一个数组里边都包含质数,比如有一个数组[2,5,7],那么此时如果我们通过把数组里边元素相乘, 得到2 * 5 * 7。
此时我们就很容易判断数组[2], [2,5,7],[2,5], 里边的元素是不是都在数组[2,5,7]中,
因为2 * 5 * 7 / 2 可以整除
2 * 5 * 7 / (2 * 5 * 7) 可以整除
2 * 5 * 7 / (2 * 5)可以整除
同时也很容易判断数组[2,3]里边不是所有的元素不是都在数组[2,5,7]中,
因为 2 * 5 * 7 / (2 * 3)不可以整除。
所以可以把规格和可选SKU都转成质数,通过这种方式来判断规格是否在可选的SKU里边,例如
type: [
// 一种规格类型,男裤是一种规格
["男裤", "女裤"],
// 一种规格类型
["黑色", "白色"],
["S", "L"],
],
这里边有三种规格类型,6种规格,那么如果把每个规格,按照质数从小到大转换,那就是
// ["男裤", "女裤", "黑色", "白色","S", "L"],
[2,3,5,7,11,13]
此时可选的SKU有
["男裤", "黑色", "L"],["男裤", "白色", "L"],["女裤", "白色", "S"],,["女裤", "白色", "L"],
那么此时可选的SKU对应的质数就可以得出
[2, 5, 13],[2, 7, 13],[3, 7, 11],[3, 7, 13]
此时如果把可选的SKU对应的质数相乘,就会得到
[2 * 5 * 13, 2 * 7 * 13 , 3 * 7 * 11,3 * 7 * 13]
当所有规格都转成质数,所有的可选SKU都转成质数以后,就可以通过质数的方法来根据当前已经选择的规格,查看其它规格是否可选或者需要disabled掉了。
- SKU算法实现
整个问题,就已经被转换为质数问题了。
原来的问题是当选择某个规格,需要根据可选的SKU,重新计算那些规格可以选择?
现在已经转换为当选择了某个质数,需要根据可选的SKU对应的质数数组,重新计算还有哪些质数可以重新选择。
那么此时就有问题了,那么如何判断某个质数,是不是可以选择呢?
——————————可以先思考一下😁——————————————————
其实可以通过查看可选的SKU对应的质数数组,查看当前的质数能否被整除,当某个质数能够被整除,就说明该质数对应的规格是可以被选择的。
比如刚开始的时候,肯定是没有任何一个质数已经被选择了,所以可以设置初始值为1,然后遍历所有的质数,就会发现
[2,3,5,7,11,13] 这些质数都可以被选择,也就是这些质数对应的规格都可以被选择,也就是男裤、女裤、黑色、白色、S、L都可以被选择。
为什么呢?
因为[2,3,5,7,11,13] 这些质数,可以被可选择的SKU质数集合,也就是[2 * 5 * 13, 2 * 7 * 13 , 3 * 7 * 11,3 * 7 * 13]整除。
所以此时初始化的时候,哪些规格可以选择的问题,就已经解决了。
下面来处理当我们选择了某个规格的情况:
假设当选择了黑裤,也就是质数2被选中的时候,此时已经被选择的规格数组就是[2], 此时就需要再次遍历所有的规格,确定当前的规格是否可以被选择。
此时需要处理两种情况。
第一种: 规格和当前选择的规格是同一种规格类型的时候,此时就需要在已经选择的规格数组中替换掉原来已经选择的规格。 比如现在已经选择了男裤,但是如果看女裤是不是可以选择的时候,此时就需要把已经被选择的规格数组[2],变成 [3] (女裤对应的规格质数是3),此时再利用[3]去遍历可选择的SKU质数集合,也就是[2 * 5 * 13, 2 * 7 * 13 , 3 * 7 * 11,3 * 7 * 13],此时发现3是可以被选择的,因为3是可以被可选择的SKU质数集合[2 * 5 * 13, 2 * 7 * 13 , 3 * 7 * 11,3 * 7 * 13]中某一项整除的。
第二种:
规格不和已经选择的规格质数数组存在同一种类型的,比如目前选择了黑裤[2], 那么此时直接把规格添加到已经选择的规格质数数组,然后再查看当前规格是否可选就可以了。
比如规格质数5、7,13 可以发现5、7、13都是可以选择的。
为什么呢?
因为2 * 5、2 * 7、2 * 13都是可以被可选择的SKU质数数组[2 * 5 * 13, 2 * 7 * 13 , 3 * 7 * 11,3 * 7 * 13]中某一项整除的。
所以通过上面两种情况,当选择了男裤,也就是质数2以后,3、5、7,13 都可以被选择。
同理当选择了[2, 5] 以后,可选择的规格变成了7, 13了
3不能选择的原因是3 * 5 不能被可选择的SKU质数数组[2 * 5 * 11, 2 * 7 * 13 , 3 * 7 * 11, 3 * 7 * 13]中任何一项整除的。
同理11一样的道理。
- 优化
其实应该还可以进行优化,还是按照前面的思路, 假设目前规格有[2,3,5,7,11,13],一共这6种规格,此时此时可选的SKU数组变成有[2 * 7 * 11, 2 * 7 * 13 , 3 * 5 * 13]
那么此时其实可以提前先把所有的规格选择都计算出来,使用一个map缓存结果。
比如其中一条可选的SKU, 男裤白色S, 此时对应的质数为 2 、7、11, 可以这么看,此时的SKU为 [2, 7, 11], 假设库存量是1
那么此时可以认为 (* 代表任何规格) [2, *, *] 的库存量为1
[*, 7, *] 的库存量为1
[*, *, 11] 的库存量为1
[2, 7, *] 的库存量为1
[2, *, 11] 的库存量为1
[*, 7, 11] 的库存量为1
[2, 7, 11] 的库存量为1
那么此时可以把* 认为是1
此时可以转换为对象如下
{
// 2 * 1 * 1: 1,
2: 1,
7: 1,
11: 1,
14: 1,
22: 1,
77: 1,
154:1
}
同理另外一条SKU [2, 7, 13], 也可以转换为
{
// 2 * 1 * 1: 1,
2: 1,
7: 1,
13: 1,
14: 1,
26: 1,
91: 1,
182:1
}
同理另外一条SKU [3, 5, 13], 也可以转换为
{
// 3 * 1 * 1: 1,
3: 1,
5: 1,
13: 1,
15: 1,
3 * 13: 1,
5 * 13: 1,
3 * 5 * 13:1
}
如果把所有的SKU转换的对象合并那么就是
{
// 2 * 1 * 1: 1,
2: 2,
3: 1,
5: 1,
7: 2,
11: 1,
13: 2,
// 3 * 5 : 1
15: 1,
// 2 * 7: 1
14: 2,
// 2 * 11: 1
22: 1,
// 2 * 13: 1
26: 1,
// 3 * 13: 1,
39: 1,
// 5 * 13: 1,
65: 1,
// 7 * 11 :1
77: 1,
// 7 * 13 : 1
91: 1,
// 2 * 7 * 11 :1
154:1,
// 2 * 7 * 13 : 1
182:1,
// 3 * 5 * 13:1
195:1
}
所以此时有了这个可选的对象,一切都变得很容易了。
这个可选的对象命名为itemMap
-
初始化的时候,遍历所有的规格,比如2、3、5、7、11、13,如果发现在itemMap中存在,那么就是可选的。 比如 2、3、5、7、11、13 都在itemMap中,所以这些对应的规格都是可选的。
-
当选择了其中一个规格,再选下一个的时候,也是查看是否在itemMap中, 比如当选择了男裤,也就是质数2的时候,此时如果想查看女裤是否可选,因为女裤和男裤是同一规格类型,所以此时应该是查看女裤对应的质数3是否是在itemMap中,如果在则是可选的,可以发现3是可选的。
当查看黑色是否可选的时候,因为黑色对应的质数是5,所以此时需要查看男裤黑色,也就是27 是不是在itemMap中,此时发现2 * 7 在itemMap中,所以此时黑色是可选的。其它规格同理可计算出。
-
当选择了男裤*黑色后,此时已经选择的质数就是2 * 5了,此时也需要查看,还有哪些规格可选,
比如此时查看女裤,因为女裤对应的质数是3,同时女裤和男裤是同一种规格类型,此时就要查看 3 * 5 是不是在itemMap中,可以发现3 * 5 在itemMap中,所以女裤可选。 同理白色对应的质数是7,那么此时就要查看2 * 7是不是在itemMap中,发现2 * 7 在 itemMap 中,所以白色也是可选的。
但是S对应的质数是11,因为 2 * 5 * 11 不在itemMap中,所以此时S不可选
L对应的质数是13,因为 2 * 5 * 13 也不在itemMap中,所以此时L也不可选。
-
整体思路
就是根据已经选择的规格,重新去查看其它的规格,根据可选的SKU,来决定当前规格是否可选。
- 其它实现方式
有的解法是使用无向图来解决,但是但是无向图无法解决边公用的问题,就是比如有a1,a2,b1,b2,c1,c2 六种规格,当可选的SKU是 [a1 * b1 *c1, a1 * b2 * c2, a2 * b1 * c2] 的时候,也会把 a1 * b1 * c2 认为是合法的SKU.