12907


javascriptで下記の２つのオブジェクトをマージしたいと思っております。  
valの値は共通しているので”A”という結果になり、childの値はそれぞれ異なるためどちらの値も残し、マージしたいのですがどのような実装が考えられますでしょうか。

ライブラリはjQueryを使用しております。

マージ元

[code]

    obj1 = {
        val : "A",
        child : [{
            child_val: 'B',
        }]
    }
    
    obj2 = {
        val : "A",
        child : [{
            child_val: 'C',
        }]
    }
    
    obj3 = {
        val : "D",
        child : [{
            child_val: 'B',
        }]
    }
    
[/code]

obj1,obj2をマージした際に期待する結果

[code]

    obj = {
        val : "A",
        child : [{
            child_val: 'B',
        },{
            child_val: 'C',
        }]
    }
    
[/code]

obj1,obj2,obj3をマージした際に期待する結果

[code]

    obj = 
    [{
        val : "A",
        child : [{
            child_val: 'B',
        },{
            child_val: 'C',
        }]
    },
    {
        val : "D"
        child : [{
            child_val: 'B',
        }]
    }]
    
[/code]

追記：  
説明のために簡略化しておりましたがかえってややこしくしてしまっていたかもしれません。  
失礼致しました。  
やりたかったこととしては、ディレクトリのパスを複数取得し、下記objのような階層構造を作るのが目的でした。

下記のように複数のパスを配列に格納していたとして、

[code]

    [
         "A/B/C/D/E",
         "A/B/C/D",
         "A/B/D",
         "B/B/C/D"
    ]
    
[/code]

最終的にobjの構造に変換する

[code]

    obj =
    [
        {
            val : "A",
            child : [{
                val : "B",
                child : [
                    {
                        val : "C",
                        child : [{
                            val : "D",
                            child : [{
                                val : "E"
                            }]
                        }]
                    },
                    {
                        val : "D"
                    }
                ]
            }]
        },
        {
            val : "B",
            child : [{
                val : "B",
                child : [
                    {
                        val : "C",
                        child : [{
                            val : "D"
                        }]
                    }
                ]
            }]
        }
    ]
    
[/code]

上記を実現するためパスごとに階層構造を作成し、最終的にマージしようとした所で躓き、質問させていただいた次第です。


CC BY-SA 3.0
2015-07-31T23:59:37.233
12902
2015-08-01T08:32:55.810
2015-08-01T03:27:27.457
10753
10753
question
javascript jquery
オブジェクトのマージについて


> ディレクトリのパスを複数取得し、下記objのような階層構造を作るのが目的

ということであれば、個別に「オブジェクト化しマージする」のではなく、「最初から目的の構造を作る」ほうが良いでしょう。その際の基本戦略は、ディレクトリを作成するようにで、実際にやってみたコードが以下になります。

[code]

    var input = [
      "A/B/C/D/E",
      "A/B/C/D",
      "A/B/D",
      "B/B/C/D"
    ];
    
    function addPath(root, path) {
      var items = path.split("/");
      var currDir = { child: root };
      for (var i = 0; i < items.length; ++i) {
        // 現在の階層に、目的の名前のサブ階層があれば移動、なければ作って移動
        var item = items[i];
        var dir = findDir(currDir, item);
        if (dir != null) {
          currDir = dir;
          continue;
        }
        currDir = makeDir(currDir, item);
      }
    }
    
    // 現在の階層から目的の名前を持ったサブ階層を探して返す。
    // 線形検索なので効率はお察し
    function findDir(curr, name) {
      if (curr.child == null) {
        return null;
      }
      for (var i = 0; i < curr.child.length; ++i) {
        var dir = curr.child[i];
        if (dir.val == name) {
          return dir;
        }
      }
      return null;
    }
    
    // 現在の階層に、サブ階層を作って返す。
    function makeDir(curr, name) {
      if (curr.child == null) {
        curr.child = [];
      }
      var dir = { val: name };
      curr.child.push(dir);
      return dir;
    }
    
    var result = [];
    
    for (var i = 0; i < input.length; ++i) {
      addPath(result, input[i]);
    }
    
    console.log(JSON.stringify(result, null, 2));
    
[/code]

この方法のポイントは `findDir` と `makeDir`
の両関数になります。目的の構造をディレクトリに見立てて、この2つの関数を定義することで、`addPath`
が作られています。ただし、この実装はやや効率がよろしくない面があります。大量のパスを追加する場合は、そのあたりを考慮して改良してみてください。


CC BY-SA 3.0
2015-08-01T03:45:57.740
12907
2015-08-01T03:51:15.750
2015-08-01T03:51:15.750
208
208
12902
answer


一度連想配列を作り、それを出力するのはいかがでしょうか？

[code]

    var path = [
         "A/B/C/D/E",
         "A/B/C/D",
         "A/B/D",
         "B/B/C/D"
    ];
    
    var root = {};
    
    for(var i=0; i<path.length; i++){
      var dirs = path[i].split('/');
      var temp = root;
      for(var j=0; j<dirs.length; j++){
        var dir = dirs[j];
        if(!(dir in temp)){
           temp[dir] = {};
        }
        temp = temp[dir];
      }
    }
    
    function output(target){
      var parent = null;
      for(var name in target){
        var x = {};
        var child = output(target[name]);
        x.val = name;
        if(child != null){
           x.child = child;
        }
        if(parent === null){
          parent = [];
        }
        parent.push(x);
      }
    
      return parent;
    }
    
    var result = output(root);
    
    $('textarea').val(JSON.stringify(result, null, "    "));
[/code]

[code]

    textarea {
      width: 100%;
      height: 200px;
    }
[/code]

[code]

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    
    <textarea></textarea>
[/code]


CC BY-SA 3.0
2015-08-01T08:17:58.667
12916
2015-08-01T08:32:55.810
2015-08-01T08:32:55.810
4191
4191
12902
answer