鍍金池/ 問(wèn)答/Java  C  HTML/ 對(duì)比兩個(gè)套餐對(duì)象,套了九層循環(huán),有什么好的優(yōu)化方法?

對(duì)比兩個(gè)套餐對(duì)象,套了九層循環(huán),有什么好的優(yōu)化方法?

如題,要對(duì)比兩個(gè)列表,列表里面是商品套餐對(duì)象,套餐里面有tag分類,分類里面有商品,商品中有不同的口味組,口味組中有不同的子口味,做了9個(gè)循環(huán),怕被組長(zhǎng)砍死,特來(lái)求助各路大神

// 遍歷第一組套餐
for (var x = 0; x < packageInfoList.length;x++){
    // 拿到第二組中的的套餐
    var newPackage = ConfigData.getGoods(true, package.id);
    // 遍歷第一組中的tag分類
    for (var y = 0; y < package.packageTag.length;y++){
        var oldTag = package.packageTag[y];
        for (var z = 0; z < newPackage.packageTag.length;z++){
            var newTag = newPackage.packageTag[z]; // 第二組中的tag分類
            if (package.packageTag.id == newPackage.packageTag.id){
                // 遍歷第一組的商品中的tag分類中的 商品
                for (var m = 0; m < oldTag.goods.length;m++){
                     // 遍歷第二組的商品中的tag分類中的 商品
                    for (var n = 0; n < newTag.goods.length;n++){
                        if (oldTag.goods[m].id === newTag.goods[n].id){
                            var flag = updateGoodAttr(oldTag.goods[m], newTag.goods[n])
                        }
                    }
                }
            }
        }
    }
}

代碼基本如上圖

[ // 套餐列表
    { // 套餐對(duì)象
       tag: [ // tag 分類列表
            { // tag對(duì)象
                goods:[ // goods 列表
                    { // good 對(duì)象
                        attr: [ // 口味組列表(如辣度)
                            { // 口味組
                                option:[ // 口味列表
                                    taste1: {},
                                    taste2: {},
                                    taste3: {},
                                    ......
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

列表數(shù)據(jù)結(jié)構(gòu)如上
而且方法updateGoodAttr中對(duì)比兩個(gè)商品,也跑了4層的循環(huán),加起來(lái)就9層了,有沒(méi)有什么好的解決方案,
由于每個(gè)屬性都存在綁定關(guān)系(這個(gè)口味就是綁定到了這個(gè)商品,這個(gè)上商品就是綁定了這個(gè)套餐),所以不能把所有的口味taste提取出來(lái)對(duì)比,這樣9層循環(huán),到了后面毫無(wú)維護(hù)性,而且性能爆炸
求助,來(lái)份神代碼 揪揪 me,
白鞋

回答
編輯回答
糖豆豆

使用 ES5 內(nèi)置數(shù)組函數(shù) + 箭頭函數(shù)隱藏 for 循環(huán)細(xì)節(jié)

packageInfoList.forEach(package => {
    var newPackage = ConfigData.getGoods(true, package.id);
    package.packageTag.forEach(tag => {
        var newTag = newPackage.packageTag.find(t => t.id === tag.id);
        if(!newTag) return;
        tag.goods.forEach(goods => {
            var newGoods = newTag.goods.find(g => g.id === goods.id);
            if(!newGoods) return;
            var flag = updateGoodAttr(goods, newGoods);
        });
    });
});

updateGoodAttr = (goods, newGoods) => {
    goods.attr.forEach(attr => {
        var newAttr = newGoods.attr.find(a => a.id === attr.id)
        attr.option.filter(taste => !!taste.select).foreach(taste => {
            var newTasteOption = newAttr.option.find(t => t.id === taste.id);
            if(newTasteOption) {
                newTasteOption.select = taste.select;
            }
        });
    });
}

提取重復(fù)的數(shù)組比對(duì)邏輯

已經(jīng)省去里很多迭代的下角標(biāo)處理里,但是代碼還是很啰嗦,頻繁重復(fù)對(duì)兩個(gè)數(shù)組進(jìn)行元素比較。干脆把這個(gè)邏輯提取出來(lái):

var match = (arr1, arr2, identifier, process) => arr1.forEach(item1 => 
    arr2.forEach(item2 => identifier(item1) === identifier(item2) && process(item1, item2))
);

傳入兩個(gè)數(shù)組,對(duì)數(shù)組元素的每個(gè)元素使用 identifier 獲取 id,如果比較成功,則調(diào)用 process 函數(shù)處理這兩個(gè)元素。例子中 identifier 都一樣,即 item => item.id.

var ID = item => item.id;

var processPackage = (p1, p2) => match(p1.packageTag, p2.packageTag, ID, processTag);

var processTag = (tag1, tag2) => match(tag1.goods, tag2.goods, ID, processGoods);

var processGoods = (goods1, goods2) => match(goods1,attr, goods2.attr, ID, processAttr);

var processAttr = (attr1, attr2) => match(attr1.options.filter(taste => !!taste.select), attr2.options, ID, processTaste);

var processTaste = (taste1, taste2) => taste2.select = taste1.select


var packageInfoList = [...], configPackageList = [...];

match(packageInfoList, configPackageList, ID , processPackage);

消除重復(fù)調(diào)用

目前代碼中還存在重復(fù)地調(diào)用 match 函數(shù),調(diào)用邏輯相似而重復(fù),如果能把這塊抽出來(lái)就好了。我們?cè)偬崛∫粋€(gè)函數(shù),用來(lái)構(gòu)造 processXXXprocessAttr比較特殊,對(duì)兩邊取子屬性的邏輯不一樣,所以提取的這個(gè)函數(shù)需要考慮 processAttr 的需求。

var subProcess = ([s1, s2], id, process) => (i1, i2) => match(s1(i1), s2(i2), id, process)

var fixSelector = f => Array.isArray(f) ? f : [f, f]
var processTaste = (taste1, taste2) => taste2.select = taste1.select
var processAttr = subProcess(
    fixSelector([item => item.options.filter(taste => !!taste.select), item => item.options]), 
    ID,
    processTaste
)
var processGoods = subProcess(fixSelector(item => item.attr), ID, processAttr)
var processTag = subProcess(fixSelector(item => item.goods), ID, processGoods)
var processPackage = subProcess(fixSelector(item => item.tag), ID, processTag)


var ID = item => item.id
match(
    packageInfoList,
    configPackageList,
    ID,
    processPackage
);

最后一步調(diào)用其實(shí)等價(jià)于:

subProcess(fixSelector([() => packageInfoList, () => configPackageList], ID, 
    subProcess( fixSelector(package => package.tag), ID
        subProcess( fixSelector(tag => tag.goods), ID,
            subProcess( fixSelector(goods => goods.attr), ID
                subProcess( fixSelector([attr => attr.options.filter(taste => !!taste.select), attr => attr.options]), ID
                    processTaste
                )
            )   
        )          
    )
)()

把掉套函數(shù)調(diào)用拉平試試?試想如下代碼:

f(a, f(b, f(c, f(d, e)))))

// 等價(jià)于

[a,b,c,d,e].reduceRight((prev, item) => f(item, prev))

于是有:

var match = (arr1, arr2, identifier, process) => arr1.forEach(item1 => 
    arr2.forEach(item2 => identifier(item1) === identifier(item2) && process(item1, item2))
)
var subProcess = ([s1, s2], id, process) => (i1, i2) => match(s1(i1), s2(i2), id, process)
// 為了傳遞 selector 的時(shí)候可以單獨(dú)給一個(gè)函數(shù)
var fixSelector = f => Array.isArray(f) ? f : [f, f]
var reducer = (prev, [selector, identifier]) => subProcess(fixSelector(selector), identifier, prev)
var process = (...items) => items.reduceRight(reducer)()


// 調(diào)用
process(
    [ [() => packageInfoList, () => configPackageList], ID], // 初始數(shù)據(jù)
    [ package => package.tag, ID], // 根據(jù)上面一項(xiàng)的元素,返回需要繼續(xù)比對(duì)的下一層數(shù)據(jù)
    [ tag => tag.goods, ID], // 把ID當(dāng)參數(shù)在每一層傳進(jìn)來(lái)是為了支持不同層取 ID 的方式不同
    [ goods => goods.attr, ID], // 再深也不怕,無(wú)非多一個(gè)參數(shù)
    // 支持兩邊不同的取值方法
    [ [attr => attr.options.filter(taste => !!taste.select), attr => attr.options], ID], 
    // 最后一個(gè)函數(shù)就是你處理深處數(shù)據(jù)的地方啦
    (taste1, taste2) => taste2.select = taste1.select 
)

雖然調(diào)用的時(shí)候挺漂亮的,但是有點(diǎn)繞。。。

2017年10月28日 10:21
編輯回答
巫婆

比這個(gè)循環(huán)長(zhǎng)的我見(jiàn)過(guò),寫(xiě)的那個(gè)人就是以前組長(zhǎng),他估計(jì)到現(xiàn)在也沒(méi)有優(yōu)化,,,,

2018年1月25日 17:16
編輯回答
法克魷

把對(duì)比方法封裝到對(duì)象里。

比如:

套餐.compare(另一個(gè)套餐)
  tag.compare(another tag)
    good.compare(another good)
      attr.compare(another attr)
        option.compare(another option)
          taste.compare(another taste)
2018年3月27日 23:49
編輯回答
蔚藍(lán)色

組長(zhǎng)沒(méi)砍死你的話,讓他寫(xiě).沒(méi)法優(yōu)化就這樣吧.

2018年4月14日 05:27
編輯回答
風(fēng)畔

能力不夠,等待大神來(lái)優(yōu)化!


不過(guò)可以先優(yōu)化細(xì)節(jié),比如保存 arr[i],緩存 length

2018年3月7日 11:37