目前电商平台的业务中,只要有商品,不可避免的会遇到 SKU (https://baike.baidu.com/item/%E6%9C%80%E5%B0%8F%E5%AD%98%E8%B4%A7%E5%8D%95%E4%BD%8D/892217?fr=aladdin) 方面功能。这篇文章就从理论到实践,从商品创建到商品购买,手把手带你实现 SKU 相关的“核心算法”。
让我们看看实际场景:
有了上图规格选中预处理,就能够帮助用户在购买商品时,直观的了解到商品是否可以购买。
在我们实际开发过程中,商品创建页会先进行规格组装,商品购买页会对规格选择做处理。规格组装通过规格组合成 SKU 集合,规格选择根据规格内容获取库存数据量,计算 SKU 是否可被选择,两者功能在电商流程中缺一不可。
根据百度百科解释的 SKU
通过上面描述,让我们把概念和实际数据关联起来,下面让我们来举个 :
现有规格
const type = ["男裤", "女裤"]
const color = ["黑色", "白色"]
const size = ["S","L"]
那么根据现有规格,可以得到所有的 SKU 为:
[
["男裤", "黑色", "S"],
["男裤", "黑色", "L"],
["男裤", "白色", "S"],
["男裤", "白色", "L"],
["女裤", "黑色", "S"],
["女裤", "黑色", "L"],
["女裤", "白色", "S"],
["女裤", "白色", "L"],
]
上述 SKU 是如何得到的呢,让我们一起看看实现思路,并且通过上面的 来计算一遍。
首先让我们来看看笛卡尔积的描述
看来笛卡尔积满足组合计算的条件,那么下面先来一波思维碰撞,先通过导图,看看怎么实现
通过上面的思维导图,可以看出这种规格组合是一个经典的排列组合,去组合每一个规格值得到最终 SKU。
那么让我们来进行代码实现,看看代码如何实现笛卡尔积。
/**
* 笛卡尔积组装
* @param {Array} list
* @returns []
*/
function descartes(list) {
// parent 上一级索引;count 指针计数
let point = {}; // 准备移动指针
let result = []; // 准备返回数据
let pIndex = null; // 准备父级指针
let tempCount = 0; // 每层指针坐标
let temp = []; // 组装当个 sku 结果
// 一:根据参数列生成指针对象
for (let index in list) {
if (typeof list[index] === 'object') {
point[index] = { parent: pIndex, count: 0 };
pIndex = index;
}
}
// 单维度数据结构直接返回
if (pIndex === null) {
return list;
}
// 动态生成笛卡尔积
while (true) {
// 二:生成结果
let index;
for (index in list) {
tempCount = point[index].count;
temp.push(list[index][tempCount]);
}
// 压入结果数组
result.push(temp);
temp = [];
// 三:检查指针最大值问题,移动指针
while (true) {
if (point[index].count + 1 >= list[index].length) {
point[index].count = 0;
pIndex = point[index].parent;
if (pIndex === null) {
return result;
}
// 赋值 parent 进行再次检查
index = pIndex;
} else {
point[index].count++;
break;
}
}
}
}
让我们看看实际的输入输出和调用结果。
那么这个经典的排列组合问题就这样解决啦。接下来,让我们再看看,如何在商品购买中,去处理商品多规格选择。
开始前回顾下使用场景
这个图片已经能很明确的展示业务需求了。结合上述动图可知,在用户每次选择了某一规格后,需要通过程序的计算去处理其他规格情况,以便给用户提供当前情况下可供选择的其他规格。
那么让我们来看看实现思路,首先在初始化中,提供可选择的 SKU,从可选择的 SKU 中去剔除不包含的规格内容,在剔除后,提供可以进行下一步选择的规格,后续在每次用户点击情况下,处理可能选中的 SKU,最终在全部规格选择完成后,得到选中的 SKU。
首先,看下什么是邻接矩阵,来自百度百科的解释
字面描述可能比较晦涩难懂,那么让我们来看看图片帮助理解,如果两个顶点互通(有连线),那么它们对应下标的值则为 1,否则为 0。
规格
const type = ["男裤", "女裤"]
const color = ["黑色", "白色"]
const size = ["S","L"]
假设总 SKU 的库存值为下面示例,可选为有库存,不可选为某项规格无库存
[
["男裤", "黑色", "S"], // S 无号
["男裤", "黑色", "L"],
["男裤", "白色", "S"], // S 无号
["男裤", "白色", "L"],
["女裤", "黑色", "S"], // S 无号
["女裤", "黑色", "L"],
["女裤", "白色", "S"], // S 无号
["女裤", "白色", "L"],
]
那么根据邻接矩阵思想,可以得到结果图:
从图中可以看出,SKU 中每两规格都可选择,那么相对的标志值为 1,否则为 0,当整条规格选中都是 1,才会使整条 SKU 链路可选。
思路是有了,但是如何通过代码去实现呢,想必大家也有各种方式去实现,那么我就介绍下自己的实现方式:集合。
高中过去好多年了,难免忘记,这里通过集合说明图一起回顾下集合的定义
上图来自百度图片
想起集合,那么计算思路算是有了,这边我们需要用集合相等的情况,去处理 SKU 和规格值的计算。
实现思维导图
集合值
,集合值由所有涉及规格对应乘积
得到的结果,在选择规格过程中,每次选择去根据集合值去反向整除规格对应值去判断是否是子集,是否为 1。我们通过集合的思想,看看核心代码吧。
计算质数方法:
/**
* 准备质数
* @param {Int} num 质数范围
* @returns
*/
getPrime: function (num) {
// 从第一个质数 2 开始
let i = 2;
const arr = [];
/**
* 检查是否是质数
* @param {Int} number
* @returns
*/
const isPrime = (number) => {
for (let ii = 2; ii < number / 2; ++ii) {
if (number % ii === 0) {
return false;
}
}
return true;
};
// 循环判断,质数数量够完成返回
for (i; arr.length < total; ++i) {
if (isPrime(i)) {
arr.push(i);
}
}
// 返回需要的质数
return arr;
}
// 上述动图入参以及返回结果展示:
// getPrime(500) return==>
// 0: (8) [2, 3, 5, 7, 11, 13, 17, 19]
// 1: (8) [23, 29, 31, 37, 41, 43, 47, 53]
// 2: (8) [59, 61, 67, 71, 73, 79, 83, 89]
// 3: (8) [97, 101, 103, 107, 109, 113, 127, 131]
// 4: (8) [137, 139, 149, 151, 157, 163, 167, 173]
// 5: (8) [179, 181, 191, 193, 197, 199, 211, 223]
// 6: (8) [227, 229, 233, 239, 241, 251, 257, 263]
初始化处理,得到第一批邻接矩阵结果:
/**
* 初始化,格式需要对比数据,并进行初始化是否可选计算
*/
init: function () {
this.light = util.cloneTwo(this.maps, true);
var light = this.light;
// 默认每个规则都可以选中,即赋值为 1
for (var i = 0; i < light.length; i++) {
var l = light[i];
for (var j = 0; j < l.length; j++) {
this._way[l[j]] = [i, j];
l[j] = 1;
}
}
// 对应结果值,此处将数据处理的方法对应邻接矩阵的思维导图
// 0: (8) [1, 1, 1, 1, 1, 1, 1, 1]
// 1: (8) [1, 1, 1, 1, 1, 1, 1, 1]
// 2: (8) [1, 1, 1, 1, 1, 1, 1, 1]
// 3: (8) [1, 1, 1, 1, 1, 1, 1, 1]
// 4: (8) [1, 1, 1, 1, 1, 1, 1, 1]
// 5: (8) [1, 1, 1, 1, 1, 1, 1, 1]
// 6: (8) [1, 1, 1, 1, 1, 1, 1, 1]
// 得到每个可操作的 SKU 质数的集合
for (i = 0; i < this.openway.length; i++) {
// 计算结果单行示例:
// this.openway[i].join('*') ==> eval(2*3*5*7*11*13*17*19)
this.openway[i] = eval(this.openway[i].join('*'));
}
// return 初始化得到规格位置,规格默认可选处理,可选 SKU 的规格对应的质数合集
this._check();
}
计算是否可选方法:
/**
* 检查是否可以选择,更新邻接矩阵对应结果值
* @param {Boolean} isAdd 是否新增状态
* @returns
*/
_check: function (isAdd) {
var light = this.light;
var maps = this.maps;
for (var i = 0; i < light.length; i++) {
var li = light[i];
var selected = this._getSelected(i);
for (var j = 0; j < li.length; j++) {
if (li[j] !== 2) {
//如果是加一个条件,只在是 light 值为 1 的点进行选择
if (isAdd) {
if (li[j]) {
light[i][j] = this._checkItem(maps[i][j], selected);
}
} else {
light[i][j] = this._checkItem(maps[i][j], selected);
}
}
}
}
return this.light;
},
/**
* 检查是否可选内容,更新邻接矩阵对应结果值
* @param {Int} item 当前规格质数
* @param {Array} selected
* @returns
*/
_checkItem: function (item, selected) {
// 拿到可以选择的 SKU 内容集合
var openway = this.openway;
var val;
// 拿到已经选中规格集合*此规格集合值
val = item * selected;
// 可选 SKU 集合反除,查询是否可选
for (var i = 0; i < openway.length; i++) {
this.count++;
if (openway[i] % val === 0) {
return 1;
}
}
return 0;
}
添加规格方法:
/** 选择可选规格后处理
* @param {array} point [x, y]
*/
add: function (point) {
point = point instanceof Array ? point : this._way[point];
// 得到选中规格对应的质数内容
var val = this.maps[point[0]][point[1]];
// 检查是否可选中
if (!this.light[point[0]][point[1]]) {
throw new Error(
'this point [' + point + '] is no availabe, place choose an other'
);
}
// 判断是否选中内容已经存在已经选择内容中
if (val in this.selected) return;
var isAdd = this._dealChange(point, val);
this.selected.push(val);
// 选择后邻接矩阵对应数据修改为 2,以做是否可选区分
this.light[point[0]][point[1]] = 2;
this._check(!isAdd);
}
移除已选规格方法:
/**
* 移除已选规格
* @param {Array} point
*/
remove: function (point) {
point = point instanceof Array ? point : this._way[point];
// 容错处理
try {
var val = this.maps[point[0]][point[1]];
} catch (e) {}
if (val) {
// 在选中内容中,定位取出需要移除规格质数
for (var i = 0; i < this.selected.length; i++) {
if (this.selected[i] == val) {
var line = this._way[this.selected[i]];
// 对应邻接矩阵内容更新为可选
this.light[line[0]][line[1]] = 1;
// 从已选内容中移除
this.selected.splice(i, 1);
}
}
// 进行重新计算
this._check();
}
}
开源代码将在 9 月中旬提供。如需,请关注微信公众号:政采云前端团队。回复 sku,即可获取开源地址。
看来老师没有骗我们,在学习中学到的经典排列组合,邻接矩阵,集合还是很有用处的。其中经典排列组合笛卡尔积思想不用死记硬背,通过理解就可以完成递归树状图的大量情况。根据邻接矩阵,可以简化空间复杂程度,通过集合思想,实现选择数据判断。
相信阅读完本篇文章的你,对于电商规格处理的两个算法已经有了大体了解。
1.上述集合计算思路借鉴文献, 详情见链接 (http://git.shepherdwind.com/sku-search-algorithm.html)。
2.另一种正则匹配实现思路文献借鉴,详情见链接 (https://gist.github.com/shepherdwind/2141756)。
3.邻接矩阵思路借鉴文献,详情见链接 (https://gist.github.com/shepherdwind/2141756)。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/A2BLVnhas49Z1pj6OubymA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。