web-dev-qa-db-ja.com

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オブジェクトを構築する必要があります。

これは基本的にホワイトリストベースであり、すべての「true」プロパティはfalseとして定義されたものをオーバーライドする必要があります。したがって、期待される結果は次のようになります。

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

クレイジーなネストされたループに依存せずにそれに対処する方法がわからない

JSBinの出発点を以下に示します。 http://jsbin.com/usaQejOJ/1/edit?js,console

ご協力いただきありがとうございます!

20

ロダッシュには、この問題をエレガントに解決するのに役立ついくつかの方法があります。

まず、 merge メソッドです。複数のソースオブジェクトを受け取り、それらのプロパティを再帰的に結合して宛先オブジェクトにします。 2つのオブジェクトが同じプロパティ名を持っている場合、後者の値が前者をオーバーライドします。

これはほとんど私たちが望んでいることです。ロールオブジェクトを1つのオブジェクトにマージします。私たちが望まないのは、そのオーバーライド動作です。 trueの値が常にfalseの値をオーバーライドするようにします。幸いにも、カスタムマージ関数を渡すことができます。lodashのmergeは、2つのオブジェクトが同じキーを持っている場合に、それを使用してマージされた値を計算します。

(後でOR値がfalsesをオーバーライドできるようにする代わりに)アイテムを論理的に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]にフラット化します。

したがって、必要なすべての引数をflattened配列にまとめ、_apply _mergeに渡します。

オブジェクトが非常に深くネストされている場合、このように再帰的にmergerを呼び出すとスタックがオーバーフローする可能性がありますが、オブジェクトの場合はこれで問題ありません。

42
adamesque