[Tutor] Manipulating Dictionary values

Steven D'Aprano steve at pearwood.info
Mon Dec 26 06:09:02 EST 2016


On Mon, Dec 26, 2016 at 01:33:37PM +0530, Sunil Tech wrote:
> Hi Team,
> 
> Dictionary is like
> 
> a = {'a': 'New', 'b': 'Two', 'l': [{'k': 'test', 'm': 'again'}, {'k':
> 'test', 'm': 'again'}]}
> 
> I am trying to modify a value in the dictionary value at a['l'] & at 'm'
> expecting it to be
> 
> a = {'a': 'New', 'b': 'Two', 'l': [{'k': 'test', 'm': 'replaced'}, {'k':
> 'test', 'm': 'replaced'}]}

Simplify the problem. The dictionary "a" is not relevant. All the 
processing is happening to the list:

[{'k': 'test', 'm': 'again'}, {'k': 'test', 'm': 'again'}]

First thing: don't use the string.replace() method unless that is what 
you *really* mean. Remember that replace works on substrings, not the 
entire string. Suppose you have:

[{'k': 'test', 'm': 'against'}, {'k': 'test', 'm': 'again'}]

If you use string.replace('again', 'replaced') then your result will be:

[{'k': 'test', 'm': 'replacedst'}, {'k': 'test', 'm': 'again'}]

which is surely not what you want.

So your first question should be: how do you change the value of a 
dict with key 'm' from 'again' to 'replaced'?

# Before
d = {'a': 'something', 'b': 'who cares?', 'm': 'again'}

# After
d = {'a': 'something', 'b': 'who cares?', 'm': 'replaced'}

And the answer is:

if d['m'] == 'again':
    d['m'] = 'replaced'


Now you just need to write a loop to do that for every dict in the list:


alist = [{'k': 'test', 'm': 'again'}, {'k': 'test', 'm': 'again'}]
for adict in alist:
    if adict['m'] == 'again':
        adict['m'] = 'replaced'
    
print(alist)


What if the list is inside a dict? It doesn't matter.

a = {'a': 'New', 
     'b': 'Two', 
     'l': [{'k': 'test', 'm': 'again'}, {'k': 'foo bar', 'm': 'again'}],
     'z': 'something else',
     }

for adict in a['l']:
    if adict['m'] == 'again':
        adict['m'] = 'replaced'

print(a)



Last one: can we do this as an expression, using a list comprehension? 
(Why do we want to?) Yes, but only by writing more complicated code and 
doing much more work, which means it will probably be slower. MUCH 
slower.

a = {'a': 'New', 
     'b': 'Two', 
     'l': [{'k': 'test', 'm': 'again'}, {'k': 'foo bar', 'm': 'again'}],
     'z': 'something else',
     }


a['l'] = [**** for adict in a['l']]

What code goes into the **** in the list comp? It has to be a function 
which takes a dict, and returns the same dict, or a copy of that dict, 
with value associated with the key 'm' conditionally replaced.

(If this sounds complicated to you, that's because it is complicated. 
Why does this have to be a list comprehension?)

I can easily write a helper function:

def modify(adict):
    if adict['m'] == 'again':
        adict['m'] = 'replaced'
    return adict

and then use:

a['l'] = [modify(adict) for adict in a['l']]

but what if you don't want the helper function? Then it becomes really 
complicated, but here is a one-liner with no dependencies and no helper 
functions needed:

a['l'] = [dict([(key, 'replaced' if value is 'again' else value) for (key, value) in adict.items()]) for adict in a['l']]

Look how much unnecessary and pointless work this does, and how hard it 
is to understand, compared to this version:

for adict in a['l']:
    if adict['m'] == 'again':
        adict['m'] = 'replaced'


One really complicated, hard to understand, slow line, versus three 
simple, easy to understand, fast lines.

Beginners often want to do things as one-liners. More experienced coders 
keep asking, why does it need to be a one-liner? How much extra work do 
you want to do just to avoid pressing the Enter key on your keyboard?





-- 
Steve


More information about the Tutor mailing list