2014-01-14 6 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's 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 не принимает массив объектов для слияния вместе; вы должны передать их в качестве аргументов. В нашем случае массив был бы ужасно удобным.

Чтобы обойти эту проблему, мы будем использовать apply метод в JavaScript, который позволяет вызвать метод, передавая свои аргументы в виде массива, а также удобный flatten метод Lodash, которая принимает массив, который может содержать вложенные массивы - как [1, [2, 3], [4, 5]] - и выравнивает его до [1, 2, 3, 4, 5].

Итак, мы соберем все необходимые нам аргументы в массиве flatten, а затем apply их до merge!

Если ваши объекты вложены очень глубоко, есть вероятность, что вы переполните стек, вызвав merger рекурсивно, как это, но для ваших объектов это должно работать нормально.

+3

Ничего себе я не мог надеяться, что ответ объяснил, что хорошо! Миллион спасибо! –

+2

OMG благодарит за этот ответ! – fuzzyvagina