这是耗费了我好几天反复试错,最终实践的一种电商产品 SKU 增改算法,也许并不是特别优雅,但确实能正常使用。本文就大概讲下思路吧,我还给这个“算法”起了个名字,叫“阶梯查找法”。

阅读本文大概需要用到这些术语,你可以结合某宝上的任意商品来代入进来。

  • 选项组:例如大小、颜色
  • 选项值:例如小、大、红色、黄色
  • SKU:例如小、红色就是其中一个,上述组合(小、大、红色、黄色)一共会有 2 * 2 = 4 个

使用场景

使用场景是创建产品的页面,我并不需要根据预设的「选项组」和「选项值」一次性的生成 SKU。而是提供前端操作,一步一步依次的添加 SKU 数据。

例如:创建“颜色”选项组,添加“红色”和“黄色”选项值,这里就生成了两个 SKU

红色
黄色

还是没看懂?不妨看看我做的两张图吧:

选项组和选项值.jpg

SKU.jpg

我在曾经的日记《电商 SKU 难题》里面也提到过,我的老朋友(巨佬)@Eric 也给了我些许思路,但我最终还是没按照他的想法(创建+排序)来做,还是用了我最先想到的这种方法。

插入选项组改变 SKU 的场景总共就三个,在这里我分别称作“魔改法”、“插针法”和“追加法,用不同示例说明具体的算法步骤,以及应该实现的效果。

魔改法

首先是魔改法,任何「选项组」的第一个「选项值」,会将所有 SKU 对应「选项组」的属性均设置为第一个「选项值」。

这是初始数据,下面的示例均会使用这个结构在不同位置进行添加。

颜色:红色
尺寸:小、中

SKU 大概有:

红色、小
红色、中

示例:插入一组,并提供第一个选项值

我要插入一个“风格”「选项组」,并在里面插入一个“经典”,应该是什么样的步骤呢?

  • “经典”在“风格”选项组的是第一条数据,所有的 SKU 默认都是“经典”款了
  • 不需要插入任何新的数据
红色、小、经典 // <- 被修改的
红色、中、经典 // <- 被修改的

插针法

只要不是层级最高的那个「选项组」(这里是颜色),都会根据上一个「选项组」的每个「选项值」,分批量多次的插入进去。

这是初始数据,下面的示例均会使用这个结构在不同位置进行添加。

颜色:红色
尺寸:小、中
风格:经典、现代

SKU 大概有:

红色、小、经典
红色、小、现代
红色、中、经典
红色、中、现代

示例一:最后一组

我要在“风格”这组「选项组」里插入一个“未来”,应该是什么样的步骤呢?

  • “未来”在“风格”选项组的上一条数据是“现代”,提取所有的“现代”复制一个出来先
  • 看看它上一个「选项组」“尺寸”的值,一共会有两处插入点。
  • 根据上一个「选项组」“尺寸”将提取到的“现代”分成两组数据
  • 根据两处插入点的位置,分别插入两组数据,就搞定了
红色、小、经典
红色、小、现代 // <- 被复制的
红色、小、未来 // <- 新增的
红色、中、经典
红色、中、现代 // <- 被复制的
红色、中、未来 // <- 新增的

因为目前程序设计是不允许直接移除 SKU 的,只能允许修改为 0 库存,所以这么做是没有问题的。

示例二:中间的一组

我要在“尺寸”这组「选项组」里插入一个“大”,应该是什么样的步骤呢?

  • “大”在“尺寸”选项组的上一条数据是“中”,提取所有的“中”复制一个出来先
  • 看看它上一个「选项组」“颜色”的值,一共会有一处插入点。
  • 根据上一个「选项组」“颜色”将提取到的“中”分成一组数据
  • 根据一处插入点的位置,分别插入一组数据,就搞定了

注意此时「颜色」只有红色一个,和上一个示例的结果对比貌似差了很多,不仔细观察的话,会很容易被误导,也是因为这个地方,我被搅乱了思路,写了两个处理逻辑,实际上只需要一个就够了(在写本文的时候发现并合并了),文章后面再详细说明

红色、小、经典
红色、小、现代
红色、中、经典 // <- 被复制的
红色、中、现代 // <- 被复制的
红色、大、经典 // <- 新增的
红色、大、现代 // <- 新增的

如果「颜色」有两个,这里就应该复制出 4 个新的了:

红色、小、经典
红色、小、现代
红色、中、经典 // <- 被复制的(组一
红色、中、现代 // <- 被复制的(组一
红色、大、经典 // <- 新增的
红色、大、现代 // <- 新增的
黄色、小、经典
黄色、小、现代
黄色、中、经典 // <- 被复制的(组二
黄色、中、现代 // <- 被复制的(组二
黄色、大、经典 // <- 新增的
黄色、大、现代 // <- 新增的

追加法

如果是最顶层的 SKU(这里是颜色),它是找不到上一层的,就只能一次性复制所有数据,放在最后面了。

这是初始数据,下面的示例均会使用这个结构在不同位置进行添加。

颜色:红色
尺寸:小、中
风格:经典、现代

SKU 大概有:

红色、小、经典
红色、小、现代
红色、中、经典
红色、中、现代

示例:最顶层的一组

我要在“颜色”这组「选项组」里插入一个“黄色”,应该是什么样的步骤呢?

  • “黄色”在“颜色”选项组的上一条数据是“红色”,提取所有的“红色”复制一个出来先
  • 由于是最顶层的那组,根据“红色”的最后一条数据,确定一处插入点(一般就是整个 SKU 数组的最后一个)
  • 由于是最顶层的那组,不需要再次分组
  • 根据一处插入点的位置,分别插入一组数据,就搞定了
红色、小、经典 // <- 被复制的
红色、小、现代 // <- 被复制的
红色、中、经典 // <- 被复制的
红色、中、现代 // <- 被复制的
黄色、小、经典 // <- 新增的
黄色、小、现代 // <- 新增的
黄色、中、经典 // <- 新增的
黄色、中、现代 // <- 新增的

已知的 Bug 和解决思路

如果一次性插入三组空白的「选项组」,第一组和第三组「选项组」均有一个「选项值」,而第二组没有「选项值」,给第三组插入第二条「选项值」,就会出现找不到对象的异常。

颜色:红色
尺寸:
风格:经典、现代(插入现代就报错)

这种问题有两种解法,一个是“继续向上”查找有「选项值」的「选项组」(还有可能撞到第一个也没有值的)要么提供前端屏蔽操作,禁止一次性插入多个空白「选项组」。鉴于这种复杂度,我还是选择后者实现会比较简单。

被误导的场景

插针法的最后一组示例从结果上来看,只需要复制所有的“现代”,并且直接插入在其下方就可以了。由于是最后一组「选项组」,符合「选项值」为“现代”的 SKU 就只会有“现代”。

而换成中间一组示例的话,符合“中”的 SKU 还包括了其下属的“经典”和“现代”,你就必须要一并复制出“经典”和“现代”至少两条数据,这才产生了“复制一组插入一组”的概念了,就在编写此文的时候,我也顺带纠正了此前代码冗余的,基于“最后一组”判断条件写的逻辑。

总结

这个场景着实是我写前端这么久第一次遇到的比较烧脑的一个坑,所以值得写文记录一下。可见适当刷编程算法题确实是能拓展思维的,只是对于我这种数学学渣来说,是确实特别的烧脑烧细胞啊!

我的头脑转的比较慢,过程确实特别痛苦,但实现出来之后的感觉就不同了,还是挺有一番成就感的。