0

I have 2 arrays A and B:

"A": [{
       "name": "test1",
       "id": "build:jenkins>test1"
    }, {
        "name": "test2",
        "id": "build:jenkins>test2"
    }, {
        "name": "maven",
        "id": "build:maven"
    }, {
        "name": "maven1",
        "id": "build:maven1"
    }]

"B": [{
        "name": "jenkins",
        "id": "build:jenkins"
    }, {
        "name": "m1",
        "id": "build:maven>m1"
    }, {
        "name": "m2",
        "id": "build:maven>m2"
    }, {
        "name": "maven3",
        "id": "build:maven3"
    }]

I am trying to get a resultant array "C" which will search for available children in both the arrays based on the "id" and give an array as:

"C":
    [{  "id": "build:jenkins",
    "children": 
        [{"name": "test1","id": "build:jenkins>test1"},
        {"name": "test2","id": "build:jenkins>test2"}
    ]
},

{   "id": "build:maven",
    "children": 
        [{"name": "m1","id": "build:maven>m1"}, 
        {"name": "m2","id": "build:maven>m2"}
    ]
},

{"id": "build:maven1","children":[]}, 
{"id": "build:maven3","children":[]}
  ]

I am trying to iterate through array A and then iterate through array B to get to the children based on the id but not able to do two way searching in both the arrays at the same time. Please help me get the result like array C.

3
  • is the separator always a symbol? Commented Dec 17, 2015 at 20:08
  • why not merge A and B? Commented Dec 17, 2015 at 20:09
  • @abc123: Yes. The separator is always a symbol. Commented Dec 17, 2015 at 20:24

2 Answers 2

1

First you need a utility function that will find array items by id:

function findArrayItemById(id, inArray) {
  var sep = '>'; // change this if you need another separator, can also be regex
  for (var i = 0; i < inArray.length; i++) {
    if (inArray[i].id === id.split(sep)[0])
      return inArray[i];
  }
  return false;
}

Then a function which merges multiple arrays into one, in the format you require:

function buildMergedArray(arrays) {
  var result = [],
      sep = '>', // change this if you need another separator, can also be regex
      found;
  for (var i = 0; i < arrays.length; i++) {
    for (var j = 0; j < arrays[i].length; j++) {
      found = findArrayItemById(arrays[i][j].id, result);
      if (found)
        found.children.push(arrays[i][j]);
      else 
        result.push({
          id: arrays[i][j].id.split('>')[0],
          children: []
        });
    }
  }
  return result;
}

Finally, you will get the desired C result like so:

var result = buildMergedArray([source.A, source.B]);
Sign up to request clarification or add additional context in comments.

3 Comments

I tried to implement your solution. It does not work if I add/remove elements from arrays A and B. Thank you for trying to help me. Appreciate it.
@SAM That's impossible; if it doesn't work you haven't used it correctly. Please show in which conditions it doesn't work: jsfiddle.net/kevinvanlierde/ta7fkwbg
Perhaps you were right. When implementing your solution I slightly modified the split condition and the arrays A and B. Today I tried back again with as-is code and it worked all good. I am sorry for the confustion. Thanks for helping me out.
0

A proposal with a temporary object and a recursive building of the new array.

var object = { "A": [{ "name": "test1", "id": "build:jenkins>test1" }, { "name": "test2", "id": "build:jenkins>test2" }, { "name": "maven", "id": "build:maven" }, { "name": "maven1", "id": "build:maven1" }], "B": [{ "name": "jenkins", "id": "build:jenkins" }, { "name": "m1", "id": "build:maven>m1" }, { "name": "m2", "id": "build:maven>m2" }, { "name": "maven3", "id": "build:maven3" }], C: [] };

void function () {
    var temp = {}, PROP = ['id', 'name'];

    function split(s) {
        return s.split(':').pop().split('>');
    }

    function f(array) {
        array.forEach(function (a) {
            var o = temp;
            split(a.id).forEach(function (b) {
                o[b] = o[b] || {};
                o = o[b];
            });
            o.id = a.id;
            o.name = a.name;
        });

    }

    function r(object, array) {
        Object.keys(object).filter(function (a) { return !~PROP.indexOf(a); }).forEach(function (k) {
            var obj = {};
            PROP.forEach(function (a) {
                if (a in object[k]) {
                    obj[a] = object[k][a];
                }
            });
            obj.children = [];
            array.push(obj);
            if (typeof object[k] === 'object') {
                r(object[k], obj.children);
            }
        });
    }

    f(object.A);
    f(object.B);
    r(temp, object.C);
}();
document.write('<pre>' + JSON.stringify(object, 0, 4) + '</pre>');

Another approach:

The children[] is getting created at every level. Is it possible to have it only at the root level like shown in array C in the question. Also is it possible to have a non-recursive solution?

var object = { "A": [{ "name": "test1", "id": "build:jenkins>test1" }, { "name": "test2", "id": "build:jenkins>test2" }, { "name": "maven", "id": "build:maven" }, { "name": "maven1", "id": "build:maven1" }], "B": [{ "name": "jenkins", "id": "build:jenkins" }, { "name": "m1", "id": "build:maven>m1" }, { "name": "m2", "id": "build:maven>m2" }, { "name": "maven3", "id": "build:maven3" }], C: [] };
void function () {
    object.C = [];
    Object.keys(object).forEach(function (k) {
        object[k].forEach(function (a) {                    
            var path = a.id.split(/[:>]/);
            if (path.length === 2) {
                a = { id: a.id, children: [] };
                object.C.some(function (b) {
                    if (b.id === path.join(':')) {
                        return true;
                    }
                }) || object.C.push(a);
                return;
            }
            object.C.some(function (b) {
                a = JSON.parse(JSON.stringify(a));
                if (b.id === path.slice(0, 2).join(':')) {
                    b.children = b.children || [];
                    b.children.some(function (c, i, aa) {
                        if (c.id === a.id) {
                            aa[i] = a;
                            return true;
                        }
                    }) || b.children.push(a);
                    return true;
                }
            }) || object.C.push({ id: path.slice(0, 2).join(':'), children: [a] });
        });
    });
}();
document.write('<pre>' + JSON.stringify(object, 0, 4) + '</pre>');

1 Comment

The children[] is getting created at every level. Is it possible to have it only at the root level like shown in array C in the question. Also is it possible to have a non-recursive solution?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.