3

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?

2
  • 3
    that isn't a nested list, it is a list of tuples. Tuples are immutable so you can't just replace the element directly in the first case. Commented Oct 25, 2016 at 16:46
  • You're right this is a list of tuples, I was trying to show a simple structure to convey the issue. Showing more nests would only add to the length of loc. I will edit this to make it a list of lists. Commented Oct 25, 2016 at 16:50

3 Answers 3

3

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].

Sign up to request clarification or add additional context in comments.

7 Comments

this only works if every level is a list, if some layers have tuples / other constructs this won't work. Probably not an issue but the question was originally posted using tuples so might be relevant for OP's use case.
@TadhgMcDonald-Jensen Well I'm answering the question as it is. And the title did say just "list" already before the edit. Yours wouldn't work for that original, either, like you said it's even somewhat impossible. Anyway, I added a more general version and it looks nicer. Thanks :-)
@StefanPochmann do you mind adding some explanation for how this works?
@LMc Hmm, I thought I did, saying it does the same as the other answer. Is it still unclear after reading reduce's documentation?
@LMc Oh well, I added some more.
|
3

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

3 Comments

A note: The OP hasn't specified the Python version (the print having only one argument would be equivalent on both, not to mention __future__ can make it a function on Py2). *nest,element=[0,1,2,3,4,5,6,7] is only legal in Py3, not Py2. On Py2, with a source list named foo, you could do either: nest = foo[:], element = nest.pop() or nest, element = foo[:-1], foo[-1].
Since the op used that notation in their question I assumed it is valid in the version of python they are using ;)
Oops. Missed that. Carry on sir! :-)
0

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']
>>> 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.