1

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?

3
  • 1
    can you give an example of what getFolder returns - for example how will look the object folder: getFolder(4, 'name2') Commented Sep 13, 2016 at 10:44
  • {id: 4, name: 'name2'} Commented Sep 13, 2016 at 10:45
  • Instead of return data; you should do results.push(data) and if you want the recursive call to append its results to the array you pass in then you don't need to use results.push on its return value. Commented Sep 13, 2016 at 11:33

2 Answers 2

1

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/

Sign up to request clarification or add additional context in comments.

Comments

0

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'
});

2 Comments

I get some reference issues here - data is different than data[searchFor.type].
Hi, what do you mean by reference issues? I was intending that if you simply replaced your searchAll function with mine, it would work. Is it throwing an exception, or returning incorrect results?

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.