Reputation: 1598
I was learning about recursion
in python and solved some common problems like factorial, iterating through nested lists etc.
While solving such problems, I came up with this challenge where you have to use recusion to traverse through a heterogeneous input(an input which contains single str elements, nested lists, dictionary etc).
So the challenge involves traversing through all the values in this input and replace a specified value with another one.
The input used here looks like this:
input = ['a', 'b', {'1':{'o':'a'}}, 'c', 'd', {'T': 'b', 'K': [1, 'a', 3, {'S':{'Z':'t'},'R':{'3':'a'}}, {'key':[66,'a',88]}, ['a', 'c']]}, ['a'], 3, 'r', 'a']
The input is a list
which itself contains list
s and dict
s, and some of these lists and dicts are nested and also have one another in themselves.
And the output that I'm expecting to get is:
# this is the output that should be got at the end, after running the code
['#', 'b', {'1':{'o':'#'}}, 'c', 'd', {'T': 'b', 'K': [1, '#', 3, {'S':{'Z':'t'},'R':{'3':'#'}}, {'key':[66,'#',88]}, ['#', 'c']]}, ['#'], 3, 'r', '#']
# exactly like input but with all 'a' replaced with '#'
# of course we can use treat change the input to string and then use replace() of string module and get the output
# but then this wont be a challenge would it?
correct = ['a', 'b', 'a', 'c', 'd', 'b', 1, 'a', 3, 't', 'a', 66, 'a', 88, 'a', 'c', 'a', 3, 'r', 'a']
The code I wrote is:
remove = 'a'
replace = 'X'
output = []
def recall(input):
for item in input:
if isinstance(item, list):
recall(item)
elif isinstance(item, dict):
for entry in item.values():
recall(entry)
else:
if isinstance(input, dict) and item in input.keys():
if input[item]==remove:
input[item]=replace
output.append(input[item])
else:
output.append(input[item])
else:
if item==remove:
item=replace
output.append(item)
else:
output.append(item)
print(item)
recall(input)
print(output)
This produces the output:
['X', 'b', 'X', 'c', 'd', 'b', 1, 'X', 3, 't', 'X', 66, 'X', 88, 'X', 'c', 'X', 3, 'r', 'X']
# a single list with all the 'a' replaced but there are no dicts with their key value pairs in it
I havn't been able to find a way to achieve the desired output. Am I doing something wrong? Or is there any way the desired output can be achieved with recursion?
Upvotes: 0
Views: 59
Reputation: 9858
You are appending all the values to a single global output variable regardless of whether they are from a nested dict or list. Instead, you need to get the function to return a value so that the function at the next level up can deal with it appropriately - appending a dict or list in the right position in the nested hierarchy. For example, you could do it this way:
def replace_values(item, replacement):
if isinstance(item, list):
return [replace_values(v, replacement) for v in item]
elif isinstance(item, dict):
return {k: replace_values(v, replacement) for k, v in item.items()}
# Alternatively, in case you need the dict keys to be replaced too:
# return {replacement.get(k, k): replace_values(v, replacement) for k, v in item.items()}
else:
return replacement.get(item, item)
input_list = ['a', 'b', {'1':{'o':'a'}}, 'c', 'd', {'T': 'b', 'K': [1, 'a', 3, {'S':{'Z':'t'},'R':{'3':'a'}}, {'key':[66,'a',88]}, ['a', 'c']]}, ['a'], 3, 'r', 'a']
print(replace_values(input_list, {"a": "#"}))
Note that the type (dict, list, or other) and ordering of the elements are the same in the return value as in the input item
. This preserves the nested structure of the input.
Upvotes: 1