LMc
LMc

Reputation: 18622

Find and replace an element in a (possibly) nested list in Python?

I have a list of items they may or may not be arbitrarily nested. I would like to replace one of the lists' elements. Either way (nested or not), I have the element's index location stored in another list.

Here's a nested list example where I would like to replace 'xyz' with something else, say 123. I have the location of 'xyz' stored in loc:

find='xyz'
replace=123
nested=[['abc',1],['xyz',2]] 
print(loc) # [1,0]

Using loc how can I substitute 'xyz' for 123?

Here is an unnested example where I would like to do the same substitution:

unnested=['abc','xyz']
print(loc) # [1]

If loc only has one element then you can simply do:

*nest,element=loc
if not nest: 
    unnested[element]=replace
else: pass # need help with this part

Is there something flexible enough to handle both cases?

Upvotes: 3

Views: 2077

Answers (3)

Stefan Pochmann
Stefan Pochmann

Reputation: 28596

This does the same as the other answer, just walks the path in a functional fashion:

reduce(getitem, loc[:-1], nested)[loc[-1]] = replace

In Python 3 you'll need to import reduce from functools. And getitem is from the operator module. If you just have lists, you could use list.__getitem__ instead.

Explanation: reduce starts with nested and replaces it with getitem(thecurrentvalue, i) for each value i in loc[:-1]. So for example if loc is [2, 4, 1, 3] then you get getitem(getitem(getitem(nested, 2), 4), 1). Which is the same as nested[2][4][1].

Upvotes: 3

Anil_M
Anil_M

Reputation: 11443

if you just have lists that has tuple or single element, we can first detect if a list has tuples and apply lambda function to replace elements as desired.

If the list has different types of tuples, we will need to add additional logic to recognize that.

find='xyz'
replace=123
mylist1=[('abc',1),('xyz',2)] # nests could be tuples or lists
mylist2=['abc','xyz']
replace_this = '123'

for mylist in mylist1,mylist2:

    for x in mylist:
        if (x[0],x[1]) in mylist:            
            modified = map(lambda (x, y): (x,y) if x!=find else  (replace_this,y) ,mylist)
            break
        else:            
            modified = map(lambda x: x if x!=find else  replace_this ,mylist)
            break

    print "modified list=", modified

output:

Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>> 
modified list= [('abc', 1), ('123', 2)]
modified list= ['abc', '123']
>>> 

Upvotes: 0

Tadhg McDonald-Jensen
Tadhg McDonald-Jensen

Reputation: 21453

ok so given loc = [0,1] you would want to preform the operation:

thing[0][1] = replace

or given loc = [1] you would preform

thing[1] = replace

or if we had overly complicated data and loc = [0,1,2,3,4,5,6,7] you would want:

thing[0][1][2][3][4][5][6][7] = replace

In any case we first need to look up each layer before the last element which we can do in a for loop like this:

*nest,element=[0,1,2,3,4,5,6,7]
layer = thing #start with the original object
for i in nest:
    layer = layer[i] #get an element from this layer
#now layer == thing[0][1][2][3][4][5][6]
layer[element] = replace

This also works when there is only one item in loc since in that case the for loop is iterating over an empty sequence so there is no need to treat that case seperately

Upvotes: 3

Related Questions