2015-10-25 33 views
2

我有設置一個JSON對象,類似這樣的東西:編輯JSON對象 - 尋找更好的功能計算策略

var settings = { 
    tab1: { 
    active: true, 
    selectOrder: 1, 
    selected: false 
    }, 
    tab2: { 
    active: true, 
    selectOrder: 2, 
    selected: false 
    }, 
    tab3: { 
    active: false, 
    selectOrder: 0, 
    selected: false 
    } 
}; 

現在,我的目標是設置「中選擇」屬性爲「真」的具有最低'selectedOrder'的活動元素。在這個例子中,我應該爲'tab1'元素設置'selected'('tab3'的selectedOrder爲0,但不是活動的)。

它背後的原因是根據其優先級(selectOrder)選擇第一個活動選項卡。

我的解決辦法是:

function selectFirstActiveTab(visibilitySettings) { 
    var selected = 1000; 
    var answer = jQuery.extend({}, visibilitySettings); 
    Object.keys(answer).forEach((key) => { 
    if (answer[key].active && answer[key].selectOrder <= selected) { 
     selected = answer[key].selectOrder; 
    } 
    }); 

    Object.keys(answer).forEach((key) => { 
    answer[key].selected = (answer[key].selectOrder === selected) ? true : false; 
    }); 
    return answer; 
}; 

雖然這種解決方案的工作,這似乎是一個糟糕的一個。

我的問題是:

  1. 是否有一個更好的 「函數式編程」 的方法呢? (例如使用map/reduce等)

  2. 我覺得我的設置對象結構不好。也許我應該選擇一個對象數組而不是這個JSON?選擇像這樣的JSON的原因是爲了更簡單地綁定到我的模板(類似於「{{settins.tab1.selected}}」 - 在我的html中查找它更簡單)。這裏應該是更好的方法?

Here is the complete example in JSBIN

+1

您可以保留obj的最小值和ref,所以您只需要循環一次。 – fuyushimoya

+0

是的,這兩個迭代是因爲我首先更新了selected = false。 –

+1

_using map/reduce .._你**不能**使用Array的'map' /'reduce'方法,因爲你有對象而不是數組。我會建議你使用JS的可用性庫,如[Underscore](http://underscorejs.org/#objects)。 – hindmost

回答

2
  1. 在地圖和減少對Object,您可以使用Underscore
function selectFirstActiveTab(visibilitySettings) { 
    // Clone input, with selected set to false. 
    var answer = _.mapObject(visibilitySettings, (item, key) => { 
    return {active: item.active, selectOrder: item.selectOrder, selected: false}; 
    }); 

    // Find the min among all value. 
    var minObj = _.reduce(answer, (min, item) => { 
    return (item.active && item.selectOrder < min.selectOrder) ? item : min; 
    }, {active: true, selectOrder: Number.MAX_SAFE_INTEGER}); 

    // Set min to selected. 
    minObj.selected = true; 
    return answer; 
}; 

jsBin

或香草的javascript:

function selectFirstActiveTab(visibilitySettings) { 
    var keys = Object.keys(visibilitySettings); 

    // Create a cloned obj, like map 
    var answer = {}; 
    keys.forEach((key) => { 
    var cloneTarget = visibilitySettings[key]; 
    answer[key] = { 
     active: cloneTarget.active, 
     selectOrder: cloneTarget.selectOrder, 
     selected: false 
    }; 
    }); 

    // Convert key => value map to an array of value, then reduce it to find min. 
    var minimun = keys.map((key) => answer[key]).reduce((min, item) => { 
    return (item.active && item.selectOrder < min.selectOrder) ? item : min; 
    }, {active: true, selectOrder: Number.MAX_SAFE_INTEGER}); 

    // Set min to selected. 
    minimun.selected = true; 
    return answer; 
}; 

jsBin

  • 這不是JSON,它只是一個普通的Object一些鍵 - 值映射,我們用它作爲Map
  • var settings = [ 
        { 
        name: 'tab1', 
        active: true, 
        selectOrder: 1, 
        selected: false 
        }, 
        { 
        name: 'tab2', 
        active: true, 
        selectOrder: 2, 
        selected: false 
        }, 
        { 
        name: 'tab3', 
        active: false, 
        selectOrder: 0, 
        selected: false 
        } 
    ]; 
    

    如香草JavaScript的支撐降低array /圖:如果輸入是一樣的陣列它可以更容易地應用上面的邏輯。然而,許多庫像lodash,underscore只是減少/地圖可用的對象。你可以只選擇最適合你需要的那個(因爲有許多這樣的lib,使你能夠映射/減少對象)。

    +0

    這就是我一直在尋找的,謝謝。我想現在我會堅持香草js。 –

    0

    試試這個:

    tmp = 0; 
    _tab = false; 
    for (tab in settings){ 
        if((settings[tab].active)){ 
         if(!tmp){ 
          tmp = settings[tab].selectOrder; 
          _tab = tab; 
         }else if(settings[tab].selectOrder < tmp){ 
          _tab = tab; 
         } 
        }; 
    } 
    settings[_tab].selected = true; 
    
    +1

    這是更「功能性」的方法嗎?這正是我試圖避免的(循環對象)並保存狀態爲 –

    +1

    在你的代碼中使用2個循環,我試圖減少循環 –

    +0

    第二個循環用於更新虛假值。我只能使用一個,這不是我的問題......我一直在尋找「功能」方法。不管怎麼說,還是要謝謝你! –

    2

    使用lodash,你可以寫:

    import { chain, transform, extend } from 'lodash'; 
    
    transformObj = (obj) => chain(settings) 
        .map((obj, key)=> ({ key, obj })) 
        .select(({ obj: { active }})=> active) 
        .min(({ obj: { selectOrder }})=> selectOrder) 
        .thru(({ key })=> transform(settings, (memo, val, _key) => { 
         memo[_key] = extend({}, val, { 
         selected: (_key == key) 
         }) 
        }, {}) 
    ) 
        .value() 
    

    > transformObj(settings) 
    { tab1: { active: true, selectOrder: 1, selected: true }, 
        tab2: { active: true, selectOrder: 2, selected: false }, 
        tab3: { active: false, selectOrder: 0, selected: false } } 
    

    上述假設ES6 importsarrow functions的可用性和論證destructuring。否則,您可以使用babel

    這是更實用,因爲transformObj是引用透明&不改變其參數。

    +0

    很酷。我正在尋找這樣的東西。問題在於它看起來比「常規」解決方案複雜且效率低下。另外,我必須添加另一個庫到我的頁面。這是更好的解決方案嗎? –

    +1

    更好的是主觀意見。就複雜而言,我發現一系列轉換在更復雜轉換的情況下更容易解密和調試 - 當然,鑑於用例有點簡單,可能是一種矯枉過正。是的,你必須添加一個庫,但lodash是我發現的無價之寶,一段時間後提供的高階函數成爲第二性質。如果您特別關注增加的膨脹,您可以選擇自定義構建中需要的特定功能。 https://lodash.com/custom-builds – lorefnon

    +1

    就低效率而言 - 迭代解決方案肯定會更有效率,但在複雜的使用情況下,高效的不可變數據結構可能會有所幫助。 – lorefnon