Omar
Omar

Reputation: 27

Find the index of nested lists in Python

I am still fairly new to Python. I have a list that includes numbers and lists of numbers:

list1 = [[3,3,2,2,1,1], 5, [2, 1], 2, [6,2,8]]

I have a function that will loop inside and determine if the item is a number or a list. If a list is detected, it will loop inside the inner list, otherwise continue.

def searchlist(ls):
    global list1
    for element in ls:
        if isinstance(element,list):
            searchlist(element)
        elif (element == 2):
            element += 3
            list1[?][?] = element

The code obviously doesn't work but I'm looking for a way I can get the values of [?] for the actual indices of 2 in the list. So in the previous example, the following items will be replaced:

list1[0][2], list1[0][3], list1[2][0], list1[3], and list1[4][1]

I want this function to be able to change the value for the global variable (list1). So for example after calling the function searchlist on the list list1, the value of list1 should be:

list1 = [[3,3,5,5,1,1], 5, [5, 1], 5, [6,5,8]]

And I want to use this function on any list in the future, so the sizes aren't constant.

Upvotes: 0

Views: 2207

Answers (2)

abarnert
abarnert

Reputation: 366113

Since you already have a recursive function, you don't need to index the top-level nested list from the top, you just need to index the current ls parameter.

For example, on the first match, ls is the same list as list1[0], so ls[2] = … does the same thing as list1[0][2] = ….

So:

def searchlist(ls):
    for i, element in enumerate(ls):
        if isinstance(element,list):
            searchlist(element)
        elif (element == 2):
            element += 3
            ls[i] = element

If you really did want to get a whole sequence of indexes, you'd need to build that up recursively, and then apply it either recursively or in a loop. Something like this:

def searchlist(ls, *, _top=None, _indexes=()):
    if _top is None: _top = ls

    for i, element in enumerate(ls):
        if isinstance(element,list):
            searchlist(element, _top=_top, _indexes=_indexes + (i,))
        elif (element == 2):
            element += 3
            node = _top
            for index in _indexes:
                node = node[index]
            node[i] = element

To understand this, notice that a multiple-indexed list as an assignment target is a little weird:

lst[a][b][c] = d

What this actually does is something like this:

_tmp = lst[a][b]
_tmp.__setitem__(c, d)

In other words, the very last index is special, but all of the indices before it are treated as in a normal expression.

So, that's why looping over node = node[index] for all indexes but the last one gives us the right thing to use with node[i] = ….

Upvotes: 3

jpp
jpp

Reputation: 164823

You don't need global. Here's a simple algorithm specific to the structure of your input:

list1 = [[3,3,2,2,1,1], 5, [2, 1], 2, [6,2,8]]

def changer(L, in_val, out_val):
    for idx, item in enumerate(L):
        if not isinstance(item, list):
            if item == in_val:
                L[idx] = out_val
        else:
            L[idx] = [i if i != in_val else out_val for i in item]
    return L

changer(list1, 2, 5)

[[3, 3, 5, 5, 1, 1], 5, [5, 1], 5, [6, 5, 8]]

Upvotes: 1

Related Questions