6

I have a dictonary that looks something like this:

{
    'key1': 
        {
            'a': 'key1', 
            'b': 'val1', 
            'c': 'val2'
        }, 
    'key2': 
        {
            'a': 'key2', 
            'b': 'val3', 
            'c': 'val4'
        }, 
    'key3': 
        {
            'a': 'key3', 
            'b': 'val5', 
            'c': 'val6'
        }
}

I trying to delete the elements in the nested dict based on the key "a" to get an output like this:

{
    'key1': 
        {
            'b': 'val1', 
            'c': 'val2'
        }, 
    'key2': 
        {
            'b': 'val3', 
            'c': 'val4'
        }, 
    'key3': 
        {
            'b': 'val5', 
            'c': 'val6'
        }
}

I wrote the following snippet for it:

for k in dict_to_be_deleted:
    del k["a"]

I keep getting Key Error: k not found. I tried the following method as well:

for i in dict_to_be_deleted:
    for k,v in i.items():
        if "a" in k:
            del i[k]

I get

Attribute Error: str object has no attribute items

But isn't it suppose to be a dictionary since dict_to_be_deleted is a nested dictionary? I am pretty confused with this. I greatly appreciate any pointers in this regard.

5 Answers 5

7

An easy way is to use dict.pop() instead:

data = {
        'key1': 
            {
            'a': 'key1', 
            'b': 'val1', 
            'c': 'val2'
            }, 
        'key2': 
            {
            'a': 'key2', 
            'b': 'val3', 
            'c': 'val4'
            }, 
        'key3': 
            {  
            'a': 'key3', 
            'b': 'val5', 
            'c': 'val6'
            }
        }

for key in data:
    data[key].pop('a', None)

print(data)

Which Outputs:

{'key1': {'b': 'val1', 'c': 'val2'}, 'key2': {'b': 'val3', 'c': 'val4'}, 'key3': {'b': 'val5', 'c': 'val6'}}

The way dict.pop() works is that first checks if the key is in the dictionary, in this case "a", and it removes it and returns its value. Otherwise, return a default value, in this case None, which protects against a KeyError.

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

Comments

5

When you're iterating over a dictionary, dict_to_be_deleted you are only iterating over the keys. So in your second attempt, your Attribute Error is because i is the key, a string not the dictionary. How you could actually perform it would be to use .values() which iterates over the values instead.

for v in dict_to_be_deleted.values():
    del v["a"]

However, personally, instead of deleting the elements, I'd suggest following Ajax's method and building a new dictionary without the missing elements. Weird mutations like what we're doing here is an easy way to get bugs.

7 Comments

How is this "weird"?
Thanks a lot forthe quickhelp. I took your advice to create a new dict as well to ensure code efficiency.
@StefanPochmann "weird" is probably the wrong word, but always try and err on the side of avoiding possible side effects. If this gets wrapped up as some part of a function, suddenly arguments you pass in are getting changed and you might not realize it. However copying the dictionary is always going to be safe (as long as you're copying it deep enough).
@Ravi You create the whole thing from scratch... for efficiency?
@StefanPochmann I guess in that case my gut was more about creating a dictionary that replicated the Counter than actual dict mutation. Clarity wins out. (The actually just use the Counter answer to that question really was the best one). My feelings, be safe unless you have a reason not to.
|
3

In this case, it may be best to filter the contents of the dictionary:

d = {
'key1': 
    {
        'a': 'key1', 
        'b': 'val1', 
        'c': 'val2'
    }, 
 'key2': 
    {
        'a': 'key2', 
        'b': 'val3', 
        'c': 'val4'
    }, 
'key3': 
    {
        'a': 'key3', 
        'b': 'val5', 
        'c': 'val6'
    }
 }
new_d = {a:{c:d for c, d in b.items() if c != 'a'} for a, b in d.items()}

Output:

{'key3': {'c': 'val6', 'b': 'val5'}, 'key2': {'c': 'val4', 'b': 'val3'}, 'key1': {'c': 'val2', 'b': 'val1'}}

Comments

1

well you can simply use two dictionary's function with list comprehension pop() & itervalues()

[value.pop('a', None) for value in d.itervalues()]

print d

output > {'key3': {'c': 'val6', 'b': 'val5'}, 'key2': {'c': 'val4', 'b': 'val3'}, 'key1': {'c': 'val2', 'b': 'val1'}}

Benefit : it do not occupy extra memory. cause we are not creating new dict here

and if you are looking for simplicity @Ajax1234 answer is more descriptive

Comments

0

Why you are deleting when you can extract what you need ? Here is collections.defaultdict(dict) approach:

data={
    'key1':
        {
            'a': 'key1',
            'b': 'val1',
            'c': 'val2'
        },
    'key2':
        {
            'a': 'key2',
            'b': 'val3',
            'c': 'val4'
        },
    'key3':
        {
            'a': 'key3',
            'b': 'val5',
            'c': 'val6'
        }
}

import collections

dict_1=collections.defaultdict(dict)
for key,value in data.items():
    for key_1,value_1 in value.items():
        if key!='a':
            dict_1[key][key_1]=value_1

print(dict_1)

output:

{
  "key1": {
    "b": "val1",
    "c": "val2"
  },
  "key2": {
    "b": "val3",
    "c": "val4"
  },
  "key3": {
    "b": "val5",
    "c": "val6"
  }
}

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.