??對于做移動端開發(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沒有庫存哄芜,則把其他元素置灰,不可點擊柬唯。
2.關(guān)鍵字定義
- 商品SKU认臊,SKU=Stock Keeping Unit(庫存量單位),就是用于描述庫存量的最小單元,比如一個商品有紅色锄奢,藍(lán)色失晴,那么紅色為一種SKU,藍(lán)色為另外一種SKU。
- 規(guī)格 商品的屬性斟薇,比如鞋子有顏色屬性师坎,尺寸屬性
- 元素 商品的屬性描述,比如鞋子的顏色有白色堪滨,藍(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格式
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;
}];
}