sku多規(guī)格屬性組合庫存判斷 - iOS

??對于做移動端開發(fā)而言,算法搀突,數(shù)據(jù)結(jié)構(gòu)似乎只用于面試裝B用刀闷,數(shù)據(jù)的查找,排序仰迁,增加甸昏,刪除,如果一個for循環(huán)不能解決徐许,那么再嵌套一層循環(huán)施蜜,如果再不行,那就再嵌套...雌隅。前段時間看到一篇文章說翻默,面試招的是高級工程師,做的事情是一個初級程序員的事恰起,似乎有點道理修械,但也不完全對,有時候還真的需要一些思路解決特殊的問題检盼。
在做商城sku庫存判斷開發(fā)的過程中肯污,就發(fā)現(xiàn)了一個使用很多層循環(huán)不能解決的問題,需要有特殊的思路。

1.想要實現(xiàn)的效果

選中某個元素的時候蹦渣,如果其他元素和當(dāng)前選擇的元素組合成的sku沒有庫存哄芜,則把其他元素置灰,不可點擊柬唯。


圖1.png
2.關(guān)鍵字定義
  1. 商品SKU认臊,SKU=Stock Keeping Unit(庫存量單位),就是用于描述庫存量的最小單元,比如一個商品有紅色锄奢,藍(lán)色失晴,那么紅色為一種SKU,藍(lán)色為另外一種SKU。
  2. 規(guī)格 商品的屬性斟薇,比如鞋子有顏色屬性师坎,尺寸屬性
  3. 元素 商品的屬性描述,比如鞋子的顏色有白色堪滨,藍(lán)色胯陋,尺寸有40碼,41碼

具體情況是這樣的袱箱, 在購物袋中(商品詳情頁中)遏乔,用戶在點擊購買或者加入購物車時需要選擇所有的規(guī)則,但是不是所有規(guī)則下的屬性都是可選的发笔,比如上面的圖片如果選擇了黑色盟萨,二段,那么鞋碼的36了讨,41是不可選的捻激,因為沒有庫存,如果按照上面的圖中的所有可以產(chǎn)生的sku數(shù)總共有多少種呢

黑色        白色
三段        二段
40       36        41

黑色 三段 50
黑色 三段 36
黑色 三段 41
黑色 二段 40
黑色 二段 36
黑色 二段 41
白色 三段 50
白色 三段 36
白色 三段 41
白色 二段 40
白色 二段 36
白色 二段 41
總共有2x2x3 = 12 種

3.服務(wù)器返回json格式
1531624561062.jpg
1531624561062.jpg

skus是所有屬性元素拼接可能產(chǎn)生的sku個數(shù)前计,包括有庫存和沒有庫存的sku胞谭,單個sku使用sku對象中的store參數(shù)判斷是否有庫存,sku對象下的select_spec_id是specs對象對應(yīng)的spec_value_id的多個元素使用‘_’拼接組合男杈,比如黑色丈屹,二段,那么拼接的select_spec_id組合為3_5伶棒。

4.常規(guī)解決思路

??初始化時當(dāng)我們想要點擊黑色旺垒,判斷三段和二段可不可以選,如果判斷三段可以選了肤无,又要鞋碼40先蒋,36,41碼是否可以選舅锄,回頭還別忘了白色是否可選鞭达。貌似需要使用三層循環(huán)判斷司忱,結(jié)果應(yīng)該可以判斷出來皇忿,但是問題是如果鞋子又增加了一個屬性畴蹭,那么理論上又得增加一層循環(huán),常規(guī)思路好像很不好解決鳍烁,邏輯難厘清叨襟,較勁腦汁,想不出什么好辦法幔荒,但是又覺得應(yīng)該有辦法糊闽,畢竟這個問題做過sku庫存判斷的前段同學(xué)都實現(xiàn)過,然后在網(wǎng)上搜了一篇簡書的文章爹梁,得到了提示http://www.reibang.com/p/7a17b4179225

5.合理的解決思路

??初始化時當(dāng)我們想要點擊黑色右犹,判斷三段是否可以點擊,那么只需要判斷黑色_三段 是否可以點擊姚垃,不需要做其他判斷念链,如果已經(jīng)選則了黑色_三段,判斷40是否可以點擊积糯,那么只需要判斷黑色_三段_40 是否可以點擊掂墓,不需要判斷其他的元素。
思路似乎就清晰了許多

結(jié)論是看成,當(dāng)我們想要判斷某個元素是否可以點擊君编,那么只需判斷當(dāng)前已經(jīng)選擇的元素和想要選擇的元素拼成的一個路徑是否存在有庫存的sku的子集中。

現(xiàn)在服務(wù)端返回的12個sku中川慌,列舉一下元素spec_value_id拼接和是否有庫存

1 黑色 三段 40         3_16_19有庫存
2 黑色 三段 36         3_6_15 有庫存
3 黑色 三段 41         3_6_20無庫存
4 黑色 二段 40         3_5_19有庫存
5 黑色 二段 36         3_5_15無庫存
6 黑色 二段 41         3_5_20無庫存
7 白色 三段 40         2_6_19無庫存
8 白色 三段 36         2_6_15有庫存
9 白色 三段 41         2_6_20無庫存
10 白色 二段 40        2_5_19無庫存
11 白色 二段 36        2_5_15無庫存
12 白色 二段 41        2_5_20有庫存
第一步吃嘿,過濾有庫存的sku

/**
 計算所有包含庫存的sku

 @param skus 服務(wù)器返回的所有的sku
 @return 有庫存的sku
 */
+ (NSMutableArray *)enoughStoreSkusWithSkus:(NSArray *)skus {
    
    NSMutableArray *enoughStoreSkus = [NSMutableArray array]; 
    
    for (NSInteger i = 0; i < skus.count; i ++) {
        
        TRCMallShopCartSkuModel *skuModel = skus[i];
        if (skuModel.store > 0) {
            
            [enoughStoreSkus addObject:skuModel];
        }        
    }
    
    return enoughStoreSkus;
}

結(jié)果為:
1 黑色 三段 40         3_16_19有庫存
2 黑色 三段 36         3_6_15 有庫存
3 黑色 二段 40         3_5_19有庫存
4 白色 三段 36         2_6_15有庫存
5 白色 二段 41         2_5_20有庫存
第二步,使用遞歸找到所有有庫存sku的冪集
/**
 獲取所有包含庫存的冪集集合

 @param skus 包含庫存的sku
 @return 去掉重復(fù)的子集的冪集集合
 */
+ (NSMutableArray *)powerSetWithSkus:(NSMutableArray *)skus {
    
    NSMutableArray *enoughStoreSkus = [self enoughStoreSkusWithSkus:skus];
    
    // 包含所有的
    NSMutableArray *allPowerSet = [NSMutableArray array];    
    
    for (NSInteger i = 0; i < enoughStoreSkus.count; i ++) {
                
        TRCMallShopCartSkuModel *skuModel = enoughStoreSkus[i];
     
        NSArray *select_spec_ids = [skuModel.select_spec_id componentsSeparatedByString:@"_"];
        
        [self powersetArray:[NSMutableArray arrayWithArray:select_spec_ids] index:0 set:@"" powerSet:allPowerSet ];        
    }    
    
    return allPowerSet;    
}


// 遞歸求單個sku的冪集
+ (void)powersetArray:(NSMutableArray *)array index:(NSInteger)index set:(NSString *)set powerSet:(NSMutableArray *)powerSet  {
    
    NSString *tempString = set;
    
    if (index >= array.count) {
        
        NSLog(@"set = %@ ==%p",set,set);
        if (set.length > 0 && ![powerSet containsObject:set]) {

            [powerSet addObject:set];            
        }         
    } else {
        
        [self powersetArray:array index:index + 1 set:tempString powerSet:powerSet singleSkuPowset:singleSkuPowset]; // 每次需要set完整的版本       
        // 每次將temp數(shù)組的部分元素加到temp中
        if (tempString.length > 0) {
            
            tempString = [tempString stringByAppendingString:[NSString stringWithFormat:@",%@",array[index]]];
            
        } else {
            
            tempString = [tempString stringByAppendingString:[NSString stringWithFormat:@"%@",array[index]]];
        }
        
        [self powersetArray:array index:index + 1 set:tempString powerSet:powerSet singleSkuPowset:singleSkuPowset]; // temp成為新的set
        // 如果powerset的是時候一直i+1就等于把set數(shù)組一直置空
    }
}

打印結(jié)果:
Printing description of allPowerSet:
<__NSArrayM 0x60000045d340>(
19,
6,
6,19,
3,
3,19,
3,6,
3,6,19,
15,
6,15,
3,15,
3,6,15,
5,
5,19,
3,5,
3,5,19,
2,
2,15,
2,6,
2,6,15,
20,
5,20,
2,20,
2,5,
2,5,20
)

第三步梦重,循環(huán)每個元素兑燥,判斷該元素是否可以點擊
// 判斷是否有庫存
+ (void)haveStoreWithSpecValues:(NSMutableArray *)specValues powerSet:(NSMutableArray *)powerSet {
    
    /*如何確定 紅 可選? 只需要確定 紅-B 可選
     如何確定 中 可選忍饰? 需要確定 白-中-B 可選
     如何確定 2G 可選贪嫂? 需要確定 白-B-2G 可選*/
    
    // 已經(jīng)有選中元素的屬性行
    NSMutableArray *selectedSpecValues = [NSMutableArray array];
    
    // 沒有選擇元素的屬性行
    NSMutableArray *withOutSelectedValus = [NSMutableArray array];
    
    for (NSInteger i = 0; i < specValues.count; i ++) {
        
        TRCMallShopCartAttributeModel *attributeModel = specValues[i];
        if (attributeModel.selected) {
            
            [selectedSpecValues addObject:attributeModel];
            
        } else {
            
            [withOutSelectedValus addObject:attributeModel];
        }
    }
    
    
    
    for (NSInteger i = 0; i < specValues.count; i ++) {
        
        TRCMallShopCartAttributeModel *attributeModel = specValues[i];
        
        NSArray *selections = [self selectionDescendingCurrentSpecPropertyModel:attributeModel selectedSpecValues:selectedSpecValues];
        
        for (NSInteger j = 0; j < attributeModel.values.count; j ++) {
            
            TRCMallShopCartElementModel *elementModel = attributeModel.values[j];
            
            // 如果是處于未選中狀態(tài),判斷當(dāng)前元素是否可選
            if (elementModel.status != 1) {
                
                NSString *spec_ids = [self getSpec_id_pathWithSpecValues:selections elementModel:elementModel];
                
                if ([powerSet containsObject:spec_ids]) {
                    
                    elementModel.status = 0;
                    
                } else {
                    
                    elementModel.status = 2;
                }
                
            } else {
                
                // 已經(jīng)選中的情況下,肯定是可選的
                elementModel.status = 1;
            }
            
        }
        
    }
}


/**
 * 獲取用戶已經(jīng)選中的路徑鏈接3艾蓝,9
 */ 
+ (NSString *)getSelectedElementIdPathWithSpecValues:(NSArray *)specValues {
    
    NSString *spec_ids = @"";
    for (NSInteger i = 0; i < specValues.count; i ++) {
        
        TRCMallShopCartAttributeModel *attributeModel = specValues[i];
        
        if (attributeModel.selected) {
        
            for (NSInteger j = 0; j < attributeModel.values.count; j ++) {
                
                TRCMallShopCartElementModel *model = attributeModel.values[j];
                
                // 已經(jīng)選中了
                if (model.status == 1) {
                    
                    NSString *spec_value_id = [NSString stringWithFormat:@"%ld",model.spec_value_id];
                    
                    if (spec_ids.length > 0) {
                        
                        spec_ids = [spec_ids stringByAppendingString:[NSString stringWithFormat:@",%@",spec_value_id]];
                        
                    } else {
                        
                        spec_ids = [spec_ids stringByAppendingString:spec_value_id];
                    }     
                }
                
            }
        }
                
    }    

    return spec_ids;    
}


/**
 * 獲取用戶已經(jīng)選中的屬性力崇,顏色,白色赢织,三段亮靴,段位:二段
 */ 
+ (NSString *)getSelectedElementPropertysWithSpecValues:(NSArray *)specValues {
    
    NSString *propertys = @"";
    for (NSInteger i = 0; i < specValues.count; i ++) {
        
        TRCMallShopCartAttributeModel *attributeModel = specValues[i];
        
        if (attributeModel.selected) {
        
            if (propertys.length == 0) {
                
                propertys = @"已選:"; 
            }
            
            propertys = [propertys stringByAppendingString:[NSString stringWithFormat:@"%@:",attributeModel.name]];
                        
            for (NSInteger j = 0; j < attributeModel.values.count; j ++) {
                
                TRCMallShopCartElementModel *model = attributeModel.values[j];
                
                // 已經(jīng)選中了
                if (model.status == 1) {
                    
                    NSString *text = model.text;
                    
                    propertys = [propertys stringByAppendingString:text];
                    
                    propertys = [propertys stringByAppendingString:@","];
                }
                
            }
        }                
    }    
    
    if ([propertys hasSuffix:@"于置,"]) {
        propertys = [propertys substringToIndex:propertys.length - 1];
    }
    return propertys;    
}



// 根據(jù)當(dāng)前的屬性元素茧吊,和已經(jīng)選擇其他屬性元素,拼接成最短路徑
+ (NSString *)getSpec_id_pathWithSpecValues:(NSArray *)specValues elementModel:(TRCMallShopCartElementModel *)elementModel {
    
    NSString *spec_ids = @"";
    
    for (NSInteger i = 0; i < specValues.count; i ++) {
        
        TRCMallShopCartAttributeModel *attributeModel = specValues[i];
        for (NSInteger j = 0; j < attributeModel.values.count; j ++) {
            
            TRCMallShopCartElementModel *model = attributeModel.values[j]; 
            
            // 沒有選擇,并且搓侄,跳出下一個循環(huán)
            if (model.status !=1 && (elementModel.spec_value_id != model.spec_value_id)) {
              
                continue;                  
            }   
            
            
            if (elementModel.spec_value_id == model.spec_value_id) {
                
                NSString *spec_value_id = [NSString stringWithFormat:@"%ld",model.spec_value_id];
                
                if (spec_ids.length > 0) {
                    
                    spec_ids = [spec_ids stringByAppendingString:[NSString stringWithFormat:@",%@",spec_value_id]];
                    
                } else {
                    
                    spec_ids = [spec_ids stringByAppendingString:spec_value_id];
                }   
                
            } else {
                
                // 已經(jīng)選中
                if (model.status == 1 && ![model.spec_id isEqualToString:elementModel.spec_id]) {
                    
                    NSString *spec_value_id = [NSString stringWithFormat:@"%ld",model.spec_value_id];
                    
                    if (spec_ids.length > 0) {
                        
                        spec_ids = [spec_ids stringByAppendingString:[NSString stringWithFormat:@",%@",spec_value_id]];
                        
                    } else {
                        
                        spec_ids = [spec_ids stringByAppendingString:spec_value_id];
                    }                
                }
            }
            
        }        
    }    
    
    return spec_ids;
}



// 把選擇好的元素和當(dāng)前需要判斷的屬性行生成一個排序數(shù)組
+ (NSArray *)selectionDescendingCurrentSpecPropertyModel:(TRCMallShopCartAttributeModel *)specPropertyModel selectedSpecValues:(NSMutableArray *)selectedSpecValues {
    
    NSMutableArray *combianSpecValues = [NSMutableArray arrayWithArray:selectedSpecValues];
    
    if (![combianSpecValues containsObject:specPropertyModel]) {
        
        [combianSpecValues addObject:specPropertyModel];
    }
    
    
    return  [combianSpecValues sortedArrayUsingComparator:^NSComparisonResult(TRCMallShopCartAttributeModel *obj1, TRCMallShopCartAttributeModel *obj2) {
        
        //因為不滿足sortedArrayUsingComparator方法的默認(rèn)排序順序瞄桨,則需要交換
        if (obj1.row > obj2.row)
        
        return NSOrderedDescending;
        
        //因為滿足sortedArrayUsingComparator方法的默認(rèn)排序順序,則不需要交換
        if (obj1.row < obj2.row)
        
        return NSOrderedAscending;
        return NSOrderedDescending;
    }];    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末讶踪,一起剝皮案震驚了整個濱河市芯侥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乳讥,老刑警劉巖柱查,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異云石,居然都是意外死亡唉工,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門汹忠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淋硝,“玉大人,你說我怎么就攤上這事错维〗钡兀” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵赋焕,是天一觀的道長参歹。 經(jīng)常有香客問我,道長隆判,這世上最難降的妖魔是什么犬庇? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮侨嘀,結(jié)果婚禮上臭挽,老公的妹妹穿的比我還像新娘。我一直安慰自己咬腕,他們只是感情好欢峰,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涨共,像睡著了一般纽帖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上举反,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天懊直,我揣著相機(jī)與錄音,去河邊找鬼火鼻。 笑死室囊,一個胖子當(dāng)著我的面吹牛雕崩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播融撞,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼盼铁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了懦铺?” 一聲冷哼從身側(cè)響起捉貌,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤支鸡,失蹤者是張志新(化名)和其女友劉穎冬念,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牧挣,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡急前,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瀑构。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裆针。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖寺晌,靈堂內(nèi)的尸體忽然破棺而出世吨,到底是詐尸還是另有隱情,我是刑警寧澤呻征,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布耘婚,位于F島的核電站,受9級特大地震影響陆赋,放射性物質(zhì)發(fā)生泄漏沐祷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一攒岛、第九天 我趴在偏房一處隱蔽的房頂上張望赖临。 院中可真熱鬧,春花似錦灾锯、人聲如沸兢榨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吵聪。三九已至,卻和暖如春领突,著一層夾襖步出監(jiān)牢的瞬間暖璧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工君旦, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留澎办,地道東北人嘲碱。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像局蚀,于是被迫代替她去往敵國和親麦锯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容