1

I have been working on a project that uses big nested lists. I can't use recursion functions because they give me the maximum recursion depth error. I want to be able to go through each item in the list and change it depending on it's type. Here is a shortened version of what I would like the program to do.

list_a = ["string", 1.0, 1.0]
list_b = [1.0, "string", 1.0]
list_c = [list_a, list_b]
def Updater(array):
    for elem in array:
        if type(elem) == str:
            print("string")
        if type(elem) == list:
            Updater(elem)
        if type(elem) == float:
            elem /= 2
Updater(list_c)
print(list_c)

I would like it to now divide each integer by 2, going on until each integer in the nested list has been divided so that the nested list is different. Since recursion functions give me the error, is there another way to do this?

dogking57
  • 11
  • 4
  • 2
    You seem to have a misconception about how names and values in Python work. `elem /= 2` does not mutate any list. If you don't want to run into these kinds of issues again, I suggest watching [this](https://www.youtube.com/watch?v=_AEJHKGk9ns) excellent video. – timgeb Jun 05 '20 at 06:22

2 Answers2

0

Not really a way around recursion for nested objects. Additionally, better use isinstance.
That said, you could use

list_a = ["string", 1.0, 1.0]
list_b = [1.0, "string", 1.0]
list_c = [list_a, list_b]

def Updater(lst):
    result = []
    for item in lst:
        if isinstance(item, list):
            result.append(Updater(item))
        elif isinstance(item, str):
            #print(item)
            result.append(item)
        elif isinstance(item, (float, int)):
            result.append(item / 2)
    return result

output = Updater(list_c)
print(output)

Which yields

[['string', 0.5, 0.5], [0.5, 'string', 0.5]]
Jan
  • 42,290
  • 8
  • 54
  • 79
0

To avoid recursion, you need to implement your own stack. Here is an example. (I added a list_d containing your list_c and a scalar, in order to show leaf elements being found at different depths.)

list_a = ["string", 1.0, 1.0]
list_b = [1.0, "string", 1.0]
list_c = [list_a, list_b]
list_d = [2.0, list_c]


def walk_list(l):

    if not l:
        return

    parent_lists = []
    parent_indices = []

    current_list = l
    current_index = 0

    while True:

        # descend from current position through lists until 
        # leaf element or empty list is reached
        while True:
            val = current_list[current_index]
            if not isinstance(val, list):
                # found leaf element (result of interest)
                yield (val,
                       parent_lists + [current_list],
                       parent_indices + [current_index])
                break
            elif not val:
                # found empty list (ignore)
                break
            else:
                # list to descend into
                parent_indices.append(current_index)
                parent_lists.append(current_list)
                current_list = val
                current_index = 0

        # increment to next position, reascending to parent
        # each time we go off the end of any list
        while True:
            current_index += 1
            if current_index < len(current_list):
                break
            if not parent_lists:
                return
            current_list = parent_lists.pop()
            current_index = parent_indices.pop()


print('Original: ', list_d)
print()

for val, lists, indices in walk_list(list_d):
    print("saw value {} at position {}".format(repr(val), indices))
    if isinstance(val, float):
        lists[-1][indices[-1]] /= 2

print('\nFinal: ', list_d)

gives:

Original:  [2.0, [['string', 1.0, 1.0], [1.0, 'string', 1.0]]]

saw value 2.0 at position [0]
saw value 'string' at position [1, 0, 0]
saw value 1.0 at position [1, 0, 1]
saw value 1.0 at position [1, 0, 2]
saw value 1.0 at position [1, 1, 0]
saw value 'string' at position [1, 1, 1]
saw value 1.0 at position [1, 1, 2]

Final:  [1.0, [['string', 0.5, 0.5], [0.5, 'string', 0.5]]]

The lists that comes back from the iterator is a list of increasingly deep sublists which are encountered in the path from the original list to the element in question, i.e. lists[0] will contain the original list and lists[-1] is the immediate parent list containing the value concerned.

Obviously if modifying the list while iterating over it, you need to be careful to only modify scalar values, not to modify its structure by appending or removing values from any list objects.

alani
  • 12,573
  • 2
  • 13
  • 23