[原] 淘宝SKU组合查询算法实现
前端有多少事情可以做,能做到多好。一直在關(guān)注各大公司UED方面的知識,他們也代表了前端的力量,而且也很樂意和大家分享,把應(yīng)用到項(xiàng)目的知識歸類整理,再寫成博客搬到網(wǎng)上來,充實(shí)這前端的內(nèi)容,也是為想追尋和學(xué)習(xí)的人提供了場所,為想接觸到一些前沿的知識提供了去處,感謝有這么一群人。大的科技公司基本都有自己的前端部門或團(tuán)隊(duì),在網(wǎng)上也能看到他們的動(dòng)態(tài),像淘寶、阿里巴巴、騰訊、百度等等。
前段時(shí)間在淘寶UED官網(wǎng)上看到一篇SKU組合查詢算法探索,當(dāng)時(shí)看過以后只是感覺挺牛的,而且講的很具體,實(shí)現(xiàn)步驟和代碼都想說的很詳細(xì),幾種算法以及算法的復(fù)雜度都很有深入的分析,挺佩服這種專研精神的,當(dāng)時(shí)只是隱約的感覺到這個(gè)算法在解決電商的商品拆分屬性選擇中可能會用到,但是具體的實(shí)現(xiàn)細(xì)節(jié)也沒進(jìn)行嘗試。
后來公司正好要做一個(gè)項(xiàng)目,而且用的就是淘寶商品數(shù)據(jù)結(jié)構(gòu),商品詳情頁是屬性選擇也和淘寶的很類似,當(dāng)時(shí)就想到了那篇文章,于是有反復(fù)看來兩三遍,試了一下上面說的第二種算法(已經(jīng)給出了源碼),實(shí)現(xiàn)起來也不麻煩,雖然例子中給出的第二種算法得到的結(jié)果只有商品數(shù)量,但是經(jīng)過修改也可以得到商品的價(jià)格,本打算這樣就可以直接用的項(xiàng)目中好了。但是在看到第二種算法的優(yōu)化后(沒有提供源碼),就想按照這種方式來實(shí)現(xiàn),也是最初萌發(fā)出來的想法一致。
第二種算法會有大量的組合,它是基于原始屬性值的結(jié)果組合和遞歸,而不是基于結(jié)果集的。其實(shí)第二種算法的優(yōu)化,基于結(jié)果集的算法實(shí)現(xiàn)起來也不麻煩,原理就是把結(jié)果集的SKU中key值進(jìn)行更小拆分組合,把拆分和組合后的結(jié)果信息放到SKUResult里面,這樣在初始化一次完成,后面的選擇可以根據(jù)這個(gè)結(jié)果集使用。把組合范圍減少到key里面,這樣能夠搜索范圍避免遞歸,而且得到的每個(gè)小的組合屬性值的結(jié)果有用信息很豐富,數(shù)量和價(jià)格都包括其中。
但是又過了一段時(shí)間以后,項(xiàng)目被擱淺了,也不知道以后能用上不能了,寫的示例也擱了許久,再不拿出來晾晾估計(jì)都該長毛變味了。
示例如下
測試地址: http://jsfiddle.net/tianshaojie/aGggS/
下載地址:http://files.cnblogs.com/purediy/sku-20140802.rar
主要JS代碼實(shí)現(xiàn)如下
var startTime = new Date().getTime(); //屬性集 var keys = [['10'],['20','21','22','23','24'],['30','31','32','33','34','35','36','37','38'],['40']];//后臺讀取結(jié)果集 var data = {"10;24;31;40": {price:366,count:46},"10;24;32;40": {price:406,count:66},"10;24;33;40": {price:416,count:77},"10;24;34;40": {price:456,count:9},"10;24;35;40": {price:371,count:33},"10;24;36;40": {price:411,count:79},"10;24;37;40": {price:421,count:87},"10;24;38;40": {price:461,count:9},"10;24;30;40": {price:356,count:59},"10;23;31;40": {price:366,count:50},"10;23;32;40": {price:406,count:9},"10;23;33;40": {price:416,count:90},"10;23;34;40": {price:456,count:10},"10;23;35;40": {price:371,count:79},"10;23;36;40": {price:411,count:90},"10;23;37;40": {price:421,count:10},"10;23;38;40": {price:461,count:9},"10;23;30;40": {price:356,count:46},"10;22;31;40": {price:356,count:27},"10;22;32;40": {price:396,count:38},"10;22;33;40": {price:406,count:42},"10;22;34;40": {price:446,count:50},"10;22;35;40": {price:361,count:25},"10;22;36;40": {price:401,count:40},"10;22;37;40": {price:411,count:43},"10;22;38;40": {price:451,count:42},"10;21;31;40": {price:366,count:79},"10;21;32;40": {price:406,count:79},"10;21;33;40": {price:416,count:10},"10;21;34;40": {price:456,count:10},"10;21;35;40": {price:371,count:87},"10;21;36;40": {price:411,count:10},"10;21;37;40": {price:421,count:10},"10;21;38;40": {price:461,count:80},"10;21;30;40": {price:356,count:43},"10;20;31;40": {price:356,count:46},"10;20;32;40": {price:396,count:49},"10;20;33;40": {price:406,count:65},"10;20;34;40": {price:446,count:10},"10;20;35;40": {price:361,count:34},"10;20;36;40": {price:401,count:41},"10;20;37;40": {price:411,count:36},"10;20;38;40": {price:451,count:42},"10;20;30;40": {price:346,count: 3} } //保存最后的組合結(jié)果信息 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放入結(jié)果集SKUResult function add2SKUResult(combArrItem, sku) {var key = combArrItem.join(";");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]};} }//初始化得到結(jié)果集 function initSKU() {var i, j, skuKeys = getObjKeys(data);for(i = 0; i < skuKeys.length; i++) {var skuKey = skuKeys[i];//一條SKU信息keyvar sku = data[skuKey]; //一條SKU信息valuevar skuKeyAttrs = skuKey.split(";"); //SKU信息key屬性值數(shù)組skuKeyAttrs.sort(function(value1, value2) {return parseInt(value1) - parseInt(value2);});//對每個(gè)SKU信息key屬性值進(jìn)行拆分組合var combArr = combInArray(skuKeyAttrs);for(j = 0; j < combArr.length; j++) {add2SKUResult(combArr[j], sku);}//結(jié)果集接放入SKUResultSKUResult[skuKeyAttrs.join(";")] = {count:sku.count,prices:[sku.price]}} }/*** 從數(shù)組中生成指定長度的組合*/ function arrayCombine(targetArr) {if(!targetArr || !targetArr.length) {return [];}var len = targetArr.length;var resultArrs = [];// 所有組合for(var n = 1; n < len; n++) {var flagArrs = getFlagArrs(len, n);while(flagArrs.length) {var flagArr = flagArrs.shift();var combArr = [];for(var i = 0; i < len; i++) {flagArr[i] && combArr.push(targetArr[i]);}resultArrs.push(combArr);}}return resultArrs; }/*** 獲得從m中取n的所有組合*/ function getFlagArrs(m, n) {if(!n || n < 1) {return [];}var resultArrs = [],flagArr = [],isEnd = false,i, j, leftCnt;for (i = 0; i < m; i++) {flagArr[i] = i < n ? 1 : 0;}resultArrs.push(flagArr.concat());while (!isEnd) {leftCnt = 0;for (i = 0; i < m - 1; i++) {if (flagArr[i] == 1 && flagArr[i+1] == 0) {for(j = 0; j < i; j++) {flagArr[j] = j < leftCnt ? 1 : 0;}flagArr[i] = 0;flagArr[i+1] = 1;var aTmp = flagArr.concat();resultArrs.push(aTmp);if(aTmp.slice(-n).join("").indexOf('0') == -1) {isEnd = true;}break;}flagArr[i] == 1 && leftCnt++;}}return resultArrs; } //初始化用戶選擇事件 $(function() {initSKU();var endTime = new Date().getTime();$('#init_time').text('init sku time: ' + (endTime - startTime) + " ms");$('.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);//選中自己,兄弟節(jié)點(diǎn)取消選中self.toggleClass('bh-sku-selected').siblings().removeClass('bh-sku-selected');//已經(jīng)選擇的節(jié)點(diǎn)var selectedObjs = $('.bh-sku-selected');if(selectedObjs.length) {//獲得組合key價(jià)格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);//用已選中的節(jié)點(diǎn)驗(yàn)證待測試節(jié)點(diǎn) underTestObjs$(".sku").not(selectedObjs).not(self).each(function() {var siblingsSelectedObj = $(this).siblings('.bh-sku-selected');var testAttrIds = [];//從選中節(jié)點(diǎn)中去掉選中的兄弟節(jié)點(diǎn)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 {//設(shè)置默認(rèn)價(jià)格$('#price').text('--');//設(shè)置屬性狀態(tài)$('.sku').each(function() {SKUResult[$(this).attr('attr_id')] ? $(this).removeAttr('disabled') : $(this).attr('disabled', 'disabled').removeClass('bh-sku-selected');})}}); });收獲
JavaScript中的對象屬性訪問是最快的了
總結(jié)
以上是生活随笔為你收集整理的[原] 淘宝SKU组合查询算法实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ios_随手篇3_关于宏的使用
- 下一篇: [转]Delphi 2010 3513正