2

I have a dictionary of key-value pairs, where the value is itself a dictionary. I would like to change the names of key values in that nested dictionary based on a predefined conversion.

I am using two lists to match up the values of the nested keys that I am trying to conver (Old_item1 should become New_item1):

comparison_list = ['Old_item1', 'Old_item2']
new_prods = ['New_item1', 'New_item2']

old_dict = {
    'Company1':
        {
            'Old_item1':
                {
                    'key1': val,
                    'key2': val
                },
            'Old_item2':
                {
                    'key1': val,
                    'key2': val
                }
        }
}

I tried this:

new_dict = {}
for i in comparison_list:
    for j in new_prods:
        new_dict['Company1'][j] = test['Company1'][i] 

I get a KeyError: KeyError: 'Company1'

The desired output for each item I add to each list is:

new_dict = {
    'Company1':
        {
            'New_item1':
                {
                    'key1': val  # old item key, val
                    'key2': val  # old item key, val
                }
        }
}
5
  • You will probably want as an intermediate step to set up a dictionary that maps the old keys to the new keys (use zip on the two lists to iterate when constructing this). Commented Dec 2, 2021 at 18:48
  • Nathaniel Ford's edit makes me sound much smarter than I am Commented Dec 2, 2021 at 19:08
  • 1
    @chasedcribbet Nonsense! You're plenty smart - I hope the edit helps teach the widely used language for this sort of thing, but I also want to be sure you don't feel the need to apologize for asking a question or being new to this particular area of knowledge. That's why we're all here! Commented Dec 2, 2021 at 19:13
  • @NathanielFord No worries. Just trying to get a laugh Commented Dec 2, 2021 at 20:16
  • @OlavAga made a good point below. The question now looks like I wanted to change the names, but what I wanted is a new, separate dict. Commented Dec 2, 2021 at 20:23

3 Answers 3

3

You can make a dictionary of mappings from the old to the new items, and then use it to create a new sub-dictionary for each company. This then needs to be wrapped inside an outer loop over companies (although here there is only one).

For example:

comparison_list = ['Old_item1', 'Old_item2']
new_prods = ['New_item1', 'New_item2']
    
old_dict = {'Company1': 
            {'Old_item1': 
             {'key1': 2, 
              'key2': 3},
             'Old_item2': 
             {'key1': 4,
              'key2': 5}}} 

key_mappings = dict(zip(comparison_list, new_prods))

new_dict = {k: {key_mappings[k1]: v1 for k1, v1 in v.items()}
            for k, v in old_dict.items()}

print(new_dict)

gives:

{'Company1': {'New_item1': {'key1': 2, 'key2': 3}, 'New_item2': {'key1': 4, 'key2': 5}}}

Here is the mappings dictionary key_mappings which we used:

{'Old_item1': 'New_item1', 'Old_item2': 'New_item2'}
Sign up to request clarification or add additional context in comments.

4 Comments

Be aware with this solution that it will not handle any keys in the old_dict that are not also in the key_mapping and give you a KeyError.
@NathanielFord Yes, that is correct. I was assuming that all the keys will be in those lists.
@NathanielFord If the keys are not present, then I guess there are different options depending on the requirements. It might be that the original key should be used - in which case a get as you suggest (here: key_mappings.get(k1, k1) in place of key_mappings[k1]) will do the job nicely. Or it might be that it should be omitted entirely, in which case an if k1 in key_mappings inside the inner dict comprehension could be used.
I agree! It's not specified in the OP, and there are different dynamics depending on which way you want to go. - which you've described quite well. :)
2

The easiest way to go about this is to re-create your dictionary with a comprehension:

new_dict = {company: update_item(v) for company, v in old_dict.items()}

Then parse out update_item to it's own function. You could do this inline but it makes it difficult to understand.

conversion_lookup = {
    'Old_item1': 'NewItem1',
    'Old_item2': 'NewItem2',
}

def update_item(item: dict) -> dict:
    return { conversion_lookup.get(k, k): v for k, v in item.items() }

new_dict = {company: update_item(v) for company, v in old_dict.items()}

For the conversion here I'm using a dictionary describing the conversion. If you need to construct this automatedly:

comparison_list = ['Old_item1', 'Old_item2']
new_prods = ['New_item1', 'New_item2']

conversion_lookup = { v: new_prods[idx] for idx, v in enumerate(comparison_list) }

The reason I like a dictionary is that you can use some_dict.get(a_value, a_default_value). Then, if your value isn't in your conversion dictionary you can fall back to the original value. That's what I'm doing here:

conversion_lookup.get(k, k)

The second k is the original item's value, which is a good thing to use if your conversion list doesn't include what you want.

2 Comments

With your for company, v in old_dict, do you mean for company, v in old_dict.items()? If you iterate over a dictionary, you will just get the keys.
Yup! I do. :) Thank you!
0

One approach is to do it step by step. First create the dicts with the new names and old keys, then remove the old keys, and finally add in the new names.

# Keep the values from the old keys
values = [old_dict['Company1'][old_name] for old_name in comparison_list]

# Remove old names, and add in the 
for new_key, value, old in zip(new_prods, values, comparison_list):
    old_dict['Company1'].pop(old)
    old_dict['Company1'][new_key] = value

7 Comments

I'm not sure what's being accomplished with the pop? They should simply be overwritten when you do the update, right?
I think you might be off by one with regards to nesting level. Running your code puts new_item1, etc. on the same level as Company1 - which is not what you want. I think this is an awkward approach for that reason.
To correct @NathanielFord's last point (subject to the existing limitation of the single hard-coded top-level key) just change old_dict.update to old_dict['Company1'].update
Generally in Python I find it as or more efficient to create a new dict and just replace the previous one - the way the underlying dynamics work it's basically the same thing and way easier to reason about. Also, alani is correct about the fix for the update issue, though you'd have to do it for each company and you'd have to put the whole thing in a loop or something similar to make that work.
My original question pointed to a new_dict. I think it may have been lost in the edits.
|

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.