views:

132

answers:

4

I have a hierarchically nested associative array. It looks like this:

A = { 
    B = { 
        C = {}, 
        D = {}, 
    }, 
    E = { 
        F = { 
            G = {} 
        } 
    }, 
    H = {} 
}

I would like to write a function that returns the "ancestors" of each key.

So:

f("A") = {"A"} 
f("B") = {"B","A"} 
f("C") = {"C","B","A"} 
f("G") = {"G","F","E","A"} 
f("fake") = {} 

I've worked out I need to use recursion, but I'm having difficulty writing the function. Could someone give me some pointers on how to write such a function?

(Please don't refer me to http://xkcd.com/138/!)

+6  A: 

Just apply a recursive depth-first search to find your specific element and return the path.

Recursive steps to construct path for element X.

  • If current element is X: return {X}
  • If current element is not X:

    1. Check all child nodes.
    2. Get the child-node that returns a valid path and add current element to it.
    3. If there is no valid child-node, return nil.
Dario
Thanks for explaining the method I should use. I've got it working nicely now.
Eric
+2  A: 
A = {
    B = {
        C = {},
        D = {},
    },
    E = {
        F = {
            G = {}
        }
    },
    H = {}
}

function f(root, find)
    -- iterate over all child values
    for k, v in pairs(root) do
        if k == find then
            -- found the match
            return({find})
        else
            -- no match, search the children
            tmp = f(root[k], find)
            if tmp ~= nil then
                table.insert(tmp, 1, k)
                return tmp
            end
        end
    end
end

print(table.concat(f(A, "G"), " "))

since you cannot retrieve the name of the highest-order table (in this case, A), you might need to nest this table into another table as in the following example:

r = {A = {
    B = {
        C = {},
        D = {},
    },
    E = {
        F = {
            G = {}
        }
    },
    H = {}
}
}

in this case, you will need to call f(r, "G"), of cause.

Henko
Good point about the outer table.That's pretty much the solution I came up with, except mine returns `{}` rather than `nil`. Also, you can scrap the `if k == find` and add a `if root[key] ~= nil` at the very beginning
Eric
A: 

This is the solution I came up with with using Dario's advice:

function checkTable(t, key)
    if t[key] then
        return {key}
    else
        for k, subtable in pairs(t) do
            local children = checkTable(subtable,key)
            if #children ~= 0 then
                table.insert(children,1,k)
                return children
            end
        end
        return {}
    end
end
Eric
+1  A: 

If there are loops in the hierarchy, you'll need to keep track of visited subtables. See the globals code in the Lua live demo.

lhf