user2915962
user2915962

Reputation: 2711

recursive loop in objects arrays

Im working with at structure that may look something like this:

  var data =  {
        "name": "McLaren",
        "drivers": [{
            "name": "BMW",
            "drivers": [{
                "name": "Subaru",
                "drivers": [{
                    "name": "Saab",
                    "drivers": [{
                        "name": "Lexus",
                        "drivers": []
                    }, {
                        "name": "Honda",
                        "drivers": []
                    }]
                }]
            }]
        }]
    }

Its an object containing a name and an array of drivers. drivers can be empty but it always exists.

Now lets say i have the following object:

{
"name": "KIA", 
"drivers":[]
}

I need to place this object at the right place. To my help I got the following:

McLaren/BMW/Subaru/saab

So the new object should be pushed to the same array as Lexus and Honda.

I´ve been going crazy trying to figure out how to do it. I have been thinking in the following lines:

I convert the path to an array:

var pathArr = ["McLaren","BMW","Subaru","saab"]

And try to use a recursive function:

myFunction(newObject){

        for (var i = 0; i < pathArr.length; i++) {
          recursiveFunc(data.drivers, pathArr[i] )
        }

        var recursiveFunc(driversArr, name) {
          for (var ix = 0; ix < driversArr.length; ix++) {
            if (driversArr[ix].name === name) {        
                recursiveFunc(driversArr[ix].drivers, "HERE I NEED pathArr[1]")

            }
          }
        } 
      }

I feel like I am pretty close but there are some pieces missing. Am I maybe thinking about recursion the wrong way or making it overly complicated? Help appreciated,thanks!

EDIT:

McLaren/BMW/Subaru/saab indicates the path to where the new object (KIA) should go. So KIA should go in the drivers-arr in the object with the name saab.

recursiveFunc() checks in which array the path-name exists and the then tries to use this objects array to go deeper into the object in order to find the last part of the path (saab, in this case). The new object should be pushed to this array.

Upvotes: 0

Views: 198

Answers (3)

Nina Scholz
Nina Scholz

Reputation: 386520

Just another solution, which takes the object and the path as array.

function find(object, path) {
    if (object.name.toLowerCase !== path.shift().toLowerCase) {
        return;
    }
    return (!path.length || object.drivers.some(function (a) {
        object = find(a, path.slice());
        return object;
    })) && object;
}

var data = { "name": "McLaren", "drivers": [{ "name": "BMW", "drivers": [{ "name": "VW", "drivers": [] }, { "name": "Subaru", "drivers": [{ "name": "Saab", "drivers": [{ "name": "Lexus", "drivers": [] }, { "name": "Honda", "drivers": [] }] }] }] }] },
    path = 'McLaren/BMW/Subaru/Saab',
    object = { "name": "KIA", "drivers": [] };

find(data, path.split('/')).drivers.push(object);
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');

Upvotes: 1

sahbeewah
sahbeewah

Reputation: 2690

Here's an idea to stand by: when the data is difficult to deal with, transform your data.

I propose an alternative solution that might be a little simpler:

Create an object which contains your path as a key, and a reference to the associated driver array as the value.

var mapping = {};
function storeMap (chain, data) {
    for (var i = 0; i < data.length; i++) {
        var updatedChain = (chain && chain + '/') + data[i].name;
        mapping[updatedChain] = data[i].drivers;
        if (data[i].drivers.length) {
            storeMap (updatedChain, data[i].drivers);
        }
    }
}

storeMap('', [data]);

Then to add your item:

var itemToAdd = { "name": "KIA", "drivers":[] };
var path = 'McLaren/BMW/Subaru/saab';
mapping[path].push(itemToAdd);

This was meant to be a once off... on the case of reusability: you'd have to run the map storing every time which isn't very efficient. What you could do is have methods that update the map as you're modifying it:

function addItem (item) {
    mapping[path].push(item);
    mapping[path + '/' + item.name] = item.drivers;
}

function removeItem (item) {
    var parentPath = item.name.split('/').slice(0, -1).join('/');
    var drivers = mapping[parentPath].drivers;
    drivers.splice(drivers.indexOf(item), 1);
    delete mapping[item.name];
}

Upvotes: 1

Ivan Gritsenko
Ivan Gritsenko

Reputation: 4236

You can run this code passing { drivers : [data] } root object as currentNode.

var insert = function(currentNode, nodeToInsert, pathArr) {
    if(pathArr.length == 0) {
        currentNode.drivers.push(nodeToInsert);
        return true;
    }
    for(var i = 0; i < currentNode.drivers.length; i++)
        if(currentNode.drivers[i].name == pathArr[0])
            return insert(currentNode.drivers[i], nodeToInsert, pathArr.splice(1));
    return false;
}

Note that in your example you have Saab in JSON and saab in path which will lead path search failure.

Upvotes: 1

Related Questions