Recursive function to loop over nth nesting depth

Viewed 103

Data I'm working with:

(I have this in XML format)

<root>

    <!-- level 1 nesting -->
    <main name="A" category="group">
        <sub name="A.1"></sub>
    </main>

    <!-- level 1 nesting -->
    <main name="B" category="group">
        <sub name="B.1"></sub>
    </main>

    <!-- level 1 nesting -->
    <main name="C" category="group">
        <sub name="C.1"></sub>
    </main>

    <!-- level 1 nesting -->
    <main name="D" category="group">
        <sub name="D.1"></sub>
    </main>

    <!-- level 2 nesting -->
    <main name="E" category="group">
        <sub name="E.1" category="group">
            <sub name="E.1.1"></sub>
        </sub>
    </main>

    <!-- level n nesting - this is what I need help with -->
    <main name="E" category="group">
        [...]
    </main>

</root>

Desired outcome

  • I want to print the above out in ULs and LIs according to their relationships shown above to produce a tree-view.
  • The nesting depths of the data I'm working with vary (e.g could be a single item or it could be 100 nesting depth - we don't know)
  • I need to write a solution that functionally caters for any level of nesting depth
  • Either tag (main or sub) with a category attribute value of "group" means it has children
  • The first level tag is always a main, the rest will be sub regardless of nesting depth

What I'm currently trying


def recursiveLoop(group)
    str = ""
    group.each { |g|

        str +=  "<ul>"
        str +=  "<li>#{g.attribute("name")}"

        if g.attribute("category").to_s == "group"
            recursiveLoop(g.css("sub"))
        else
            str += "</li></ul>"
        end
    }
    return str
end

Problem with my solution

After calling itself, the current iteration terminates (as expected) and goes one level deeper. However, I now can't close the <li> nor the <ul> tags as I no longer know where they started.

Edit for CamiloVA Pic

2 Answers

This works

def recursiveLoop(group)
    str = ""
    group.each { |g|

        str +=  "<li>#{g.attribute("name")}"

        if g.attribute("category").to_s == "group"
            str += "<ul>"
            str += recursiveLoop(g.css("> sub"))
            str += "</ul>"
        end
        str += "</li>"
    }
    return str
end

Two things, though:

  1. Have a main <ul> and </ul> tag wrapped around the result of recursiveLoop in the markup. Something like this:
<%= sanitize "<ul>" + recursiveLoop(@groups) + "</ul>" %>

This is only to provide a root node without getting tangled in the other objects being looped over.

  1. Be sure to specify > sub in the css selector of the XML variable g (forgot to mention I'm using Nokogiri) to restrict all sub items to their immediate parents.

As @simplelime says, you can put the close tag after the if block, and the return value of function should be concatenated to the same initial str, so you will get all the recursive childs inside each main node; and at end of execution it will return the big resulting string.

def recursiveLoop(group)
    str = ""
    group.each { |g|

        str +=  "<ul>"
        str +=  "<li>#{g.attribute("name")}"

        if g.attribute("category").to_s == "group"
            str += recursiveLoop(g.css("sub"))
        end
        str += "</li></ul>"
    }
    return str
end
Related