0

I am trying to create a function that can iterate through an object and match nodes names against a certain serach string, then if a match is found it should retrun the relevant path of the matching child node like "root/child/child/match"

This is what i've got so far:

function findNode (obj, str)
{
    var root = "root/", regx = new RegExp(str, "gi");
    function test(node) {return regx.test(node)};
    function isObject(obj) {return typeof(obj) == "object"}
    function hasSOs(obj) //check for existance of sub objects
    {
        if (isObject(obj)) for (var i in obj) if (isObject(obj[i])) return true;
        return false;
    }

    function searchDCs(obj) //search direct objects
    {
        for (var i in obj) if (test(i)) return i;
        return false;
    }

    function iterate(node)
    {
        if (searchDCs(node)) return root + searchDCs;
        else if (hasSOs(node))
        {
            for (var i in node)
            {
                if (isObject(node[i]))
                {
                    if (searchDCs(node[i])) return root + i + "/" + searchDCs(node[i]);
                    else 
                    {
                        if (iterate(node[i])) return root + i + "/" + iterate(node[i]);
                        else return false;
                    }
                }
            }
        }
        else return false;
    }
    console.log(iterate(obj));
}

It doesn't seems to be working properly, any help would be appreciated.

Edit:

  • I need it to return all search results not just the first match.
  • I need it to iterate through sub arrays and return result like root/child/child[3]/match where [3] is the 3d child in the root.child.child <- array(0, 1, 2, {object}).
razz
  • 9,770
  • 7
  • 50
  • 68
  • [Check the answers to this question](http://stackoverflow.com/questions/3454526/how-to-calculate-the-xpath-position-of-an-element-using-javascript) – Laoujin Jun 04 '14 at 16:06
  • @Laoujin the link is irrelevant, i'm trying to iterate through a `javascript object` not `xml` or `html` – razz Jun 04 '14 at 16:10
  • What is the problem with the code you've got so far? – Laoujin Jun 04 '14 at 16:13
  • It returns "false" at the end of the path, it return only one result and i also want it to iterate through array type of children nodes. – razz Jun 04 '14 at 16:17

1 Answers1

1

This is perhaps the most important part of my answer:

UnitTesting (and/or TDD) is perfect for this kind of stuff: It's isolated and you can subject your code with increasingly difficult inputs.

Such approach would continue to drive you forward while making sure that everything that used to work still works. Just tinkering with your code until 'it works' didn't get you there in this case...

The solution

(because it sounded like fun)

You couldn't really tell me what exactly the problem was, so I started anew :)

You edited your question.. I changed the code to get all occurences (not just the first one) but haven't implemented the specific syntax for Arrays.

function findNode2(obj, str) {
    var results = [],
        regx = new RegExp(str, "gi");

    function innerFind(obj, str, currentPath) {
        var propName,
            prop,
            result;

        for (propName in obj) {
            if (regx.test(propName)) {
                results.push(currentPath + propName + "/");
            }

            prop = obj[propName];
            if (typeof prop === "object") {
                innerFind(prop, str, currentPath + propName + "/");
            }
        }
    }

    if (typeof obj === "object") {
        innerFind(obj, str, "root/");
    }

    return results;
}

Update for Arrays:

function findNode2(obj, str) {
    var results = [],
        regx = new RegExp(str, "gi");

    function innerFind(obj, str, currentPath) {
        var i,
            propName,
            prop,
            result;

        if (toString.call(obj) === "[object Array]") {
            for (i = 0; i < obj.length; i++) {
                if (typeof obj[i] === "object") {
                    innerFind(obj[i], str, currentPath + "[" + i + "]/");
                }
            }

        } else {
            for (propName in obj) {
                if (regx.test(propName)) {
                    results.push(currentPath + propName + "/");
                }

                prop = obj[propName];
                if (typeof prop === "object") {
                    innerFind(prop, str, currentPath + propName + "/");
                }
            }
        }
    }

    if (typeof obj === "object") {
        innerFind(obj, str, "root/");
    }

    return results;
}
Laoujin
  • 9,962
  • 7
  • 42
  • 69
  • Thanks for the tip about unitTesting, i'm getting `RangeError: Maximum call stack size exceeded` error! does the object size matter? – razz Jun 04 '14 at 17:03
  • The size of the object shouldn't matter, but the level of nesting does. How 'deep' is your object? (If your call stack has flooded, you have some really badass object I think :) Implementing this without recursion (which is what causes the RangeError) is also possible: but it's going to be alot more code then what is there now... – Laoujin Jun 04 '14 at 17:06
  • Yah it's quite deeply nested, it's a whole API i'm searching within for some property names. – razz Jun 04 '14 at 17:09
  • Take a look at [this](http://james.padolsey.com/jquery/). It's specifically for jQuery but you can no doubt tweak it to use your third party library as input instead. – Laoujin Jun 04 '14 at 17:12
  • Consider posting what you really want to do (ie examine a third party library) as a new question. It might yield some interesting results... – Laoujin Jun 04 '14 at 17:15
  • The `jquery source viewer` looks very interesting, it does exactly what i'm looking for. I'm gonna try to tailor it for my needs and if i failed then i'll post a new question ;). thanks for the help though, i will accept your answer because it works with normal objects :). – razz Jun 04 '14 at 17:20