淘宝SKU组合查询算法实现

来源:互联网 发布:ipad淘宝下载 编辑:程序博客网 时间:2024/06/11 02:42

前端有多少事情可以做,能做到多好。一直在关注各大公司UED方面的知识,他们也代表了前端的力量,而且也很乐意和大家分享,把应用到项目的知识归类整理,再写成博客搬到网上来,充实这前端的内容,也是为想追寻和学习的人提供了场所,为想接触到一些前沿的知识提供了去处,感谢有这么一群人。大的科技公司基本都有自己的前端部门或团队,在网上也能看到他们的动态,像淘宝、阿里巴巴、腾讯、百度等等。

前段时间在淘宝UED官网上看到一篇SKU组合查询算法探索,当时看过以后只是感觉挺牛的,而且讲的很具体,实现步骤和代码都想说的很详细,几种算法以及算法的复杂度都很有深入的分析,挺佩服这种专研精神的,当时只是隐约的感觉到这个算法在解决电商的商品拆分属性选择中可能会用到,但是具体的实现细节也没进行尝试。

后来公司正好要做一个项目,而且用的就是淘宝商品数据结构,商品详情页是属性选择也和淘宝的很类似,当时就想到了那篇文章,于是有反复看来两三遍,试了一下上面说的第二种算法(已经给出了源码),实现起来也不麻烦,虽然例子中给出的第二种算法得到的结果只有商品数量,但是经过修改也可以得到商品的价格,本打算这样就可以直接用的项目中好了。但是在看到第二种算法的优化后(没有提供源码),就想按照这种方式来实现,也是最初萌发出来的想法一致。

第二种算法会有大量的组合,它是基于原始属性值的结果组合和递归,而不是基于结果集的。其实第二种算法的优化,基于结果集的算法实现起来也不麻烦,原理就是把结果集的SKU中key值进行更小拆分组合,把拆分和组合后的结果信息放到SKUResult里面,这样在初始化一次完成,后面的选择可以根据这个结果集使用。把组合范围减少到key里面,这样能够搜索范围避免递归,而且得到的每个小的组合属性值的结果有用信息很丰富,数量和价格都包括其中。

但是又过了一段时间以后,项目被搁浅了,也不知道以后能用上不能了,写的示例也搁了许久,再不拿出来晾晾估计都该长毛变味了。

示例如下

测试地址: http://jsfiddle.net/tianshaojie/aGggS/embedded/result/

主要JS代码实现如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//原始属性集
varkeys = [ [10, 11, 12], [20, 21], [30, 31, 32] ];
//测试结果集
vardata = {
    "10;20;30": {
        price: 100,
        count: 1
    },
    "10;20;31": {
        price: 111,
        count: 2
    },
    "11;20;30": {
        price: 122,
        count: 1
    },
    "10;21;31": {
        price: 133,
        count: 2
    },
    "10;21;32": {
        price: 144,
        count: 9
    }
}
//保存最后的组合结果信息
varSKUResult = {};
//获得对象的key
functiongetObjKeys(obj) {
    if (obj !== Object(obj))throw new TypeError('Invalid object');
    var keys = [];
    for (varkey inobj)
        if (Object.prototype.hasOwnProperty.call(obj, key))
           keys[keys.length] = key;
    return keys;
}
//把组合的key放入结果集SKUResult
functionadd2SKUResult(key, sku) {
    if(SKUResult[key]) {//SKU信息key属性·
        SKUResult[key].count += sku.count;
        SKUResult[key].prices.push(sku.price);
    } else{
        SKUResult[key] = {
           count : sku.count,
           prices : [sku.price]
        };
    }
}
//对一条SKU信息进行拆分组合
functioncombineSKU(skuKeyAttrs, cnum, sku) {
    var len = skuKeyAttrs.length;
    for(vari = 0; i < len; i++) {
        var key = skuKeyAttrs[i];
        for(varj = i+1; j < len; j++) {
           if(j + cnum <= len) {
               var tempArr = skuKeyAttrs.slice(j, j+cnum); //安装组合个数获得属性值·
               var genKey = key + ";" + tempArr.join(";");//得到一个组合key
               add2SKUResult(genKey, sku);
           }
        }
    }
}
//初始化得到结果集
functioninitSKU() {
    var i, j, skuKeys = getObjKeys(data);
    for(i = 0; i < skuKeys.length; i++) {
        var skuKey = skuKeys[i];//一条SKU信息key
        var sku = data[skuKey];//一条SKU信息value
        var skuKeyAttrs = skuKey.split(";");//SKU信息key属性值数组
        var len = skuKeyAttrs.length;
        //对每个SKU信息key属性值进行拆分组合
        for(j = 0; j < len; j++) {
           //单个属性值作为key直接放入SKUResult
           add2SKUResult(skuKeyAttrs[j], sku);
           //对本组SKU信息key属性进行组合,组合个数为j
           (j > 0 && j < len-1) && combineSKU(skuKeyAttrs, j, sku);
        }
        //结果集接放入SKUResult
        SKUResult[skuKey] = {
           count:sku.count,
           prices:[sku.price]
        }
    }
}

收获

JavaScript中的对象属性访问是最快的了

 

全部代码:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title> - jsFiddle demo by tianshaojie</title>
 
  <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
  <link rel="stylesheet" type="text/css" href="/css/normalize.css">
 
 
  <link rel="stylesheet" type="text/css" href="/css/result-light.css">
 
  <style type='text/css'>
    .bh-sku-selected {color: red;}
  </style>
 


<script type='text/javascript'>//<![CDATA[
$(window).load(function(){
//原始属性集
var keys = [[10, 11, 12],
            [20, 21],
            [30, 31, 32]
           ];
//测试结果集
var data = {
    "10;20;30": {
        price: 100,
        count: 1
    },
    "10;20;31": {
        price: 111,
        count: 2
    },
    "11;20;30": {
        price: 122,
        count: 1
    },
    "10;21;31": {
        price: 133,
        count: 2
    },
    "10;21;32": {
        price: 144,
        count: 9
    }
}
//保存最后的组合结果信息
var SKUResult = {};
//获得对象的key
function getObjKeys(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj)
        if (Object.prototype.hasOwnProperty.call(obj, key))
            keys[keys.length] = key;
    return keys;
}
//把组合的key放入结果集SKUResult
function add2SKUResult(key, sku) {
    if(SKUResult[key]) {//SKU信息key属性·
        SKUResult[key].count += sku.count;
        SKUResult[key].prices.push(sku.price);
    } else {
        SKUResult[key] = {
            count : sku.count,
            prices : [sku.price]
        };
    }
}
//对一条SKU信息进行拆分组合
function combineSKU(skuKeyAttrs, cnum, sku) {
    var len = skuKeyAttrs.length;
    for(var i = 0; i < len; i++) {
        var key = skuKeyAttrs[i];
        for(var j = i+1; j < len; j++) {
            if(j + cnum <= len) {
                var tempArr = skuKeyAttrs.slice(j, j+cnum);    //安装组合个数获得属性值·
                var genKey = key + ";" + tempArr.join(";");    //得到一个组合key
                add2SKUResult(genKey, sku);
            }
        }
    }
}
//初始化得到结果集
function initSKU() {
    var i, j, skuKeys = getObjKeys(data);
    for(i = 0; i < skuKeys.length; i++) {
        var skuKey = skuKeys[i];//一条SKU信息key
        var sku = data[skuKey];    //一条SKU信息value
        var skuKeyAttrs = skuKey.split(";"); //SKU信息key属性值数组
        var len = skuKeyAttrs.length;

        //对每个SKU信息key属性值进行拆分组合
        for(j = 0; j < len; j++) {
            //单个属性值作为key直接放入SKUResult
            add2SKUResult(skuKeyAttrs[j], sku);
            //对本组SKU信息key属性进行组合,组合个数为j
            (j > 0 && j < len-1) && combineSKU(skuKeyAttrs, j, sku);
        }

        //结果集接放入SKUResult
        SKUResult[skuKey] = {
            count:sku.count,
            prices:[sku.price]
        }
    }
}
//初始化用户选择事件
$(function() {
    initSKU();
    $('.sku').each(function() {
        var self = $(this);
        var attr_id = self.attr('attr_id');
        if(!SKUResult[attr_id]) {
            self.attr('disabled', 'disabled');
        }
    }).click(function() {
        var self = $(this);
        self.toggleClass('bh-sku-selected').siblings().removeClass('bh-sku-selected');

        var selectedObjs = $('.bh-sku-selected');
        if(selectedObjs.length) {
            //获得组合key价格
            var selectedIds = [];
            selectedObjs.each(function() {
                selectedIds.push($(this).attr('attr_id'));
            });
            selectedIds.sort(function(value1, value2) {
                return parseInt(value1) - parseInt(value2);
            });
            var len = selectedIds.length;
            var prices = SKUResult[selectedIds.join(';')].prices;
            var maxPrice = Math.max.apply(Math, prices);
            var minPrice = Math.min.apply(Math, prices);
            $('#price').text(maxPrice > minPrice ? minPrice + "-" + maxPrice : maxPrice);
           
            //用已选中的节点验证待测试节点 underTestObjs
            $(".sku").not(selectedObjs).not(self).each(function() {
                var siblingsSelectedObj = $(this).siblings('.bh-sku-selected');
                var testAttrIds = [];//从选中节点中去掉选中的兄弟节点
                if(siblingsSelectedObj.length) {
                    var siblingsSelectedObjId = siblingsSelectedObj.attr('attr_id');
                    for(var i = 0; i < len; i++) {
                        (selectedIds[i] != siblingsSelectedObjId) && testAttrIds.push(selectedIds[i]);
                    }
                } else {
                    testAttrIds = selectedIds.concat();
                }
                testAttrIds = testAttrIds.concat($(this).attr('attr_id'));
                testAttrIds.sort(function(value1, value2) {
                    return parseInt(value1) - parseInt(value2);
                });
                if(!SKUResult[testAttrIds.join(';')]) {
                    $(this).attr('disabled', 'disabled').removeClass('bh-sku-selected');
                } else {
                    $(this).removeAttr('disabled');
                }
            });
        } else {
            //设置默认价格
            $('#price').text('--');
            //设置属性状态
            $('.sku').each(function() {
                SKUResult[$(this).attr('attr_id')] ? $(this).removeAttr('disabled') : $(this).attr('disabled', 'disabled').removeClass('bh-sku-selected');
            })
        }
    });
});
});//]]> 

</script>


</head>
<body>
  <div>
属性1:
<input type="button" class="sku" attr_id="10" value="10"/>
<input type="button" class="sku" attr_id="11" value="11"/>
<input type="button" class="sku" attr_id="12" value="12"/>
</div>

<div>
属性2:
<input type="button" class="sku" attr_id="20" value="20"/>
<input type="button" class="sku" attr_id="21" value="21"/>
</div>

<div>
属性3:
<input type="button" class="sku" attr_id="30" value="30"/>
<input type="button" class="sku" attr_id="31" value="31"/>
<input type="button" class="sku" attr_id="32" value="32"/>
</div>

<span id="price">--</span> </br>
 
</body>


</html>

原创粉丝点击