How To Get The Indexes Of An Element Of A Nested List Of N Dimensions

- 1 answer

I am new at Python (programming in general) and I have some problem that I dont know how to get around. I have many different nested lists following this pattern:

Listi = [string0, string1, ..., stringn, [list0], [list1], ..., [listn]]

(The list contains a number of strings always positioned at the begining continued by a number of lists always positioned afterwards)

The lists cointained in the first list have the same exact structure as the first list. The lists can have any number of lists inside them.

What I would like to code is a function that given a random element (a random string) finds the indexes of such element inside the list so that this element can be called from the principal list that contains it.

I would like to get the best possible way to accomplish that in terms of number of operations, but any solution at all would be immensely appreciated.

Heres some example: Imagine I have this two lists:

l1 = ['Node_50', ['Node_48', 'Node_23'], ['Node_22', ['Node_44'], ['Node_7', 'Node_40']]]
l2 = ['Node_50', ['Node_48', 'Node_23', ['Node_12', 'Node_3'], ['Node_20']], ['Node_22', ['Node_44'], ['Node_7', 'Node_40']]]

I would like to ge a function like this:

def functionfinder(list, element):

such that:

indexes = functionfinder(l1, "Node_40")

indexes would be a tuple (2, 2, 1) because:

l1[2][2][1] = Node_40


Since you are searching nested lists, a recursive routine would be best. Since you want good speed, the routine should exit as soon as the desired item is found (rather than continuing the search or looking for another one). The following should work if you actually use only lists as your containers for your strings.

def functionfinder(asequence, value, indexes_tuple=()):
    for ndx, item in enumerate(asequence):
        if item == value:  
            return indexes_tuple + (ndx,) # found it here: return the answer
        if isinstance(item, list):
            result = functionfinder(item, value, indexes_tuple + (ndx,))
            if result:  
                return result # found it below: stop looking for it
    return ()  # desired value not found yet

l1 = ['Node_50',
      ['Node_48', 'Node_23'],
      ['Node_22', ['Node_44'], ['Node_7', 'Node_40']]
l2 = ['Node_50',
      ['Node_48', 'Node_23', ['Node_12', 'Node_3'], ['Node_20']],
      ['Node_22', ['Node_44'], ['Node_7', 'Node_40']]

indexes = functionfinder(l1, "Node_40")

The printout from that code is what you want:

(2, 2, 1)

If you have the indexes and you want to access the value that was searched, you could do

result = l1
for ndx in indexes:
    result = result[ndx]

Now result holds the value you searched for.

This code could be generalized to handle other kinds of sequences such as tuples and not just lists. You would need to be careful to not recurse into a string, since Python's strings are also sequences--of characters. You want a tuple as the return value, so my code also uses them. Using an immutable type as the return value does have advantages. But using a mutable type, such as a list, might speed up the execution speed. As it is now, each new list in your parameter requires constructing a complete new tuple of indexes. Using a list could mean that you just need to append a single new index and may speed up the code. Ask if either of those possible improvements appeals to you.

Note that, since Python does not have a built-in immediate way to stop a recursive function, my code uses a returned empty tuple to note that the desired item has not yet been found. When the item is found, a non-empty tuple result is returned which signals all levels of the recursion to stop as soon as possible. This complicates the code somewhat.