user99999
user99999

Reputation: 2024

Pushing to array and returning it from the recursive function

I want to iterate over a structure, push chosen nodes to an array and return all of them.

var structure = {
    folder: getFolder(1, 'name1'),
    children: [
        {
            folder: getFolder(2, 'name2'),
            children: [
                {
                    folder: getFolder(4, 'name2'),
                    children: []
                }
            ]
        },
        {
            folder: getFolder(3, 'name3'),
            children: []
        }
    ]
};

So for example, if folder node matches getFolder(x, 'name2'), I would get an array of two elements:

folder: getFolder(2, 'name2'),
children: [
    {
        folder: getFolder(4, 'name2'),
        children: []
    }
]

and

folder: getFolder(4, 'name2'),
children: []

Because both match the given criteria. The function I came up with is:

var searchAll = function (data, searchFor, results) {
    results = results || [];
    if (data[searchFor.type] != undefined &&
        data[searchFor.type][searchFor.index].indexOf(searchFor.value) !== -1) {
        return data;
    }
    if (data.children != null) {
        var result = null;
        for (var i = 0; result == null && i < data.children.length; i++) {
            results.push(searchAll(data.children[i], searchFor, results));
        }
    }
    return results;
};

searchAll(structure, {
    type: 'folder',
    index: 'name',
    value: 'name2'
});

But it returns undefined. How should I do this?

Upvotes: 1

Views: 5896

Answers (2)

skyline3000
skyline3000

Reputation: 7913

The key to building up an array with recursion is the concat() method, which will properly return a copy of the array all the way up the recursion stack.

In the example below, objects that match your criteria get added in with push(), while child objects are searched through recursively and their results are concatenated to the result array. For simplicity I used the results of what your getFolder()function would return in the data:

var structure = {
  folder: {id:1, name:'name1'}, //getFolder(1, 'name1'),
  children: [{
    folder: {id:2, name:'name2'}, //getFolder(2, 'name2'),
    children: [{
      folder: {id:4, name:'name2'}, //getFolder(4, 'name2'),
      children: []
    }]
  }, {
    folder: {id:3, name:'name3'}, //getFolder(3, 'name3'),
    children: []
  }]
};

function searchAll(object, criteria) {
  var i, j, result = [];

  for (i in object) {
    if (i === criteria.type && object[i][criteria.index] === criteria.value) {
      result.push(object);
    } else if (i === 'children' && object[i].length > 0) {
      for (j = 0; j < object[i].length; j++) {
        result = result.concat(searchAll(object[i][j], criteria));
      }
    }
  }

  return result;
}

console.log(searchAll(structure, {type: 'folder', index: 'name', value: 'name2'}));

Edit: link to JSFiddle because it looks like the SO code snippet stops the recursion, the results should be correct (2 objects with the data you wanted) https://jsfiddle.net/fswmxk7h/

Upvotes: 1

TomW
TomW

Reputation: 4002

The main issue is that you are bailing out and not appending to results when you find a match (return data;). This is a hopefully simpler and working version. You also have a problem that you are both passing 'results' to the recursion, and pushing the resulting array onto results, which isn't what you wanted. This is hopefully correct for the style you are going for (untested):

var searchAll = function (data, searchFor, results) {
    results = results || [];
    if (data[searchFor.type] &&
        data[searchFor.type][searchFor.index].indexOf(searchFor.value) !== -1) {
        results.push(data); // now carry on an recurse children
    }
    if (data.children) {
        for (var i = 0; i < data.children.length; i++) {
            // use results arg to avoid array creation on recursion:
            searchAll(data.children[i], searchFor, results);
        }
    }
    return results;
};

searchAll(structure, {
    type: 'folder',
    index: 'name',
    value: 'name2'
});

Upvotes: 0

Related Questions