jon
jon

Reputation: 1581

Recursive javascript function to return array

I have an array of objects. The objects may have a property called children which inturn would include an array of objects. It is effectively a tree of objects.

var my_array_of_objects=[
  {
    "object_info": "blah blah",
    "children": [
      {
        "object_info": "blah blah",
        "children": [
          {
            "object_info": "blah blah"
          }
        ]
      }
    ]
  },
  {
    "object_info": "blah blah"
  },
  {
    "object_info": "blah blah"
  }
]

I am trying to recursively loop through the my_array_of_objects to return an array that will remove the tree architecture and give each object a relationship_id and a parent_relationship_id, however I am having a problem with the relationship_id getting set before the recursions happen and I can't see why... I assume it is a scoping issue?

  function sort_relationships(objects, return_objects, parent_relationship_id, relationship_id){
      $.each( objects, function( field_key, object ){
        relationship_id++;
        var new_object={};
        new_object.relationship_id=relationship_id;
        new_object.parent_relationship_id=parent_relationship_id;
        new_object.object_info=object.object_info;
        // PUSH OBJECT TO THE ARRAY TO BE RETURNED
        return_objects.push(new_object);
        // IF THE OBJECT HAS CHILDREN THEN RUN THE FUNCTION AGAIN WITH IT'S OBJECTS
        if(object.children instanceof Array){
          sort_relationships(object.children,return_objects,new_object.relationship_id,relationship_id);
        }
      });
      return return_objects;
  }

var my_new_array=sort_relationships(my_array_of_objects,[],0,0);

Based on the code above I would expect to have a unique relationship_id for each new object, however it is returning the following.

[
  {
    "relationship_id": 1,
    "parent_relationship_id": 0,
    "object_info": "blah blah"
  },
  {
    "relationship_id": 2,
    "parent_relationship_id": 1,
    "object_info": "blah blah"
  },
  {
    "relationship_id": 3,
    "parent_relationship_id": 2,
    "object_info": "blah blah"
  },
  {
    "relationship_id": 2,
    "parent_relationship_id": 0,
    "object_info": "blah blah"
  },
  {
    "relationship_id": 3,
    "parent_relationship_id": 0,
    "object_info": "blah blah"
  }
]

I would expect to have the following:

[
  {
    "relationship_id": 1,
    "parent_relationship_id": 0,
    "object_info": "blah blah"
  },
  {
    "relationship_id": 2,
    "parent_relationship_id": 1,
    "object_info": "blah blah"
  },
  {
    "relationship_id": 3,
    "parent_relationship_id": 2,
    "object_info": "blah blah"
  },
  {
    "relationship_id": 4,
    "parent_relationship_id": 0,
    "object_info": "blah blah"
  },
  {
    "relationship_id": 5,
    "parent_relationship_id": 0,
    "object_info": "blah blah"
  }
]

Upvotes: 0

Views: 1687

Answers (2)

Bergi
Bergi

Reputation: 664599

You're doing relationship_id++ on a variable that is local to each recursive call, and is not reflected at the caller. You either need to return the updated value, or just use a free variable:

function sort_relationships(objects) {
    var return_objects = [];
    var relationship_id = 0;
    function recurse(objects, parent_relationship_id) {
        $.each(objects, function(field_key, object) {
            relationship_id++;
            var new_object = {
                relationship_id: relationship_id,
                parent_relationship_id: parent_relationship_id,
                object_info: object.object_info
            };
            // PUSH OBJECT TO THE ARRAY TO BE RETURNED
            return_objects.push(new_object);
            // IF THE OBJECT HAS CHILDREN THEN RUN THE FUNCTION AGAIN WITH IT'S OBJECTS
            if (Array.isArray(object.children)) {
                recurse(object.children, new_object.relationship_id);
            }
        });
    }
    recurse(objects, 0);
    return return_objects;
}

var my_new_array=sort_relationships(my_array_of_objects);

Upvotes: 1

trincot
trincot

Reputation: 350310

You could use reduce for this, and for the id (where things go wrong), you could add to the parent id the count of already accumulated objects:

function flatten(arr, parentId = 0) {
    if (!arr) return [];
    return arr.reduce ( function (acc, obj) {
        var rel = {
            object_info: obj.object_info,
            relationship_id: parentId + acc.length + 1,
            parent_relationship_id: parentId
        };
        return acc.concat([rel], flatten(obj.children, rel.relationship_id));
    }, []);
}

// sample data
var my_array_of_objects=[{
    "object_info": "blah blah",
    "children": [{
        "object_info": "blah blah",
        "children": [{
            "object_info": "blah blah"
          }
        ]
      }
    ]
  }, {
    "object_info": "blah blah"
  }, {
    "object_info": "blah blah"
  }
];

// convert
var result = flatten(my_array_of_objects);
// output
console.log(result);

Upvotes: 1

Related Questions