2014-01-14 39 views
18

注意:我在lodash下提出了這個問題,因爲我非常確定它可以幫助我很好地解決這個問題,但是並沒有把它放在一邊現在Lodash:構建單個對象 - 合併/重寫屬性

我有描述不同的用戶角色和他們的權限對象;

我有類似定義的「這樣的」 10-15角色(這並不反映應用程序代碼,但問題本身):

var role1 = { 
    views: { 
     v1: {access: true}, 
     v2: {access: false}, 
     v#: {access: false} 
    } 
} 

var role2 = { 
    views: { 
     v1: {access: false}, 
     v2: {access: true}, 
     v3: {access: true}, 
    } 
} 

連接將有多個角色的用戶;在這個例子中,它可能是['role1', 'role2'],在這個我需要構建一個單一的permissions對象,它將是所有用戶角色中定義的所有道具的組合。

它基本上是基於白名單-,所有的「真」的屬性應該重寫被定義爲虛假的東西。因此,預期的結果應該是:

permissions = { 
    views: { 
     v1: {access: true}, 
     v2: {access: true}, 
     v2: {access: true} 
    } 
} 

我也不太清楚如何應對一個不依賴於瘋狂的嵌套循環

下面是JSBin起點:http://jsbin.com/usaQejOJ/1/edit?js,console

感謝您的幫助!

回答

37

Lodash有幾個方法,這將有助於完美地解決了這個問題。

首先,在merge方法。它需要多個源對象,並遞歸地將它們的屬性合併到一個目標對象中。如果兩個對象具有相同的屬性名稱,則後者的值將覆蓋前者。

這幾乎是我們想要的東西;它會將你的角色對象合併成一個對象。我們不想要的是重寫行爲;我們希望true的值始終覆蓋false。幸運的是,你可以傳遞一個自定義合併函數,當兩個對象具有相同的鍵時,lodash的merge將使用它來計算合併值。

我們將編寫一個自定義函數邏輯OR您的物品放在一起(而不是讓後來false值覆蓋true秒),因此,如果任一值是true,最終合併的價值將是true

因爲我們的目標是嵌套的,我們需要確保我們的自定義合併功能時,它的比較布爾值不僅會OR。比較對象時,我們希望對它們的屬性進行正常合併(再次使用我們的自定義函數進行合併)。它看起來是這樣的:

function do_merge(roles) { 

    // Custom merge function ORs together non-object values, recursively 
    // calls itself on Objects. 
    var merger = function (a, b) { 
    if (_.isObject(a)) { 
     return _.merge({}, a, b, merger); 
    } else { 
     return a || b; 
    } 
    }; 

    // Allow roles to be passed to _.merge as an array of arbitrary length 
    var args = _.flatten([{}, roles, merger]); 
    return _.merge.apply(_, args); 
} 

do_merge([role1, role2, role3]); 

Lodash在一個其他的方式幫助了:你可以從文檔注意到_.merge不接受對象數組合併到一起;你必須將它們作爲參數傳遞。不過,在我們的例子中,一個數組非常方便。

爲了解決這個問題,我們將使用JavaScript的apply方法,它允許你通過傳遞它的參數作爲數組來調用一個方法,並Lodash的方便flatten方法,它可能包含嵌套數組的數組 - 像[1, [2, 3], [4, 5]] - 並將其壓平成[1, 2, 3, 4, 5]

所以我們將收集我們需要的所有參數在flatten ed數組中,然後apply他們到merge

如果你的對象嵌套得非常深,你可能會像這樣遞歸調用merger來溢出堆棧,但是對於你的對象,這應該工作得很好。

+3

哇,我不能希望答案解釋得那麼好!萬分感謝! –

+2

OMG非常感謝這個答案! – fuzzyvagina