-2

I have a dictionary like:

dictionary = {
    "key1" : 
        {
         "key11" : ["a", "b"],
         "key12" : "c"
        },
    "key2": "d",
    "key3": 
        {
         "key31" : ["e", "f"],
         "key32" : "g"
        }
}

and I would like to create another dictionary, with parts of first dictionary. The goal of this is to enable the user of my script to pass the keys he wants to be extracted from old file to the new file. I would like the new dictionary to be shaped for example like:

dictionary_bis = {
    "key1" : 
        {
          "key11" : ["a", "b"]
        },
    "key3" : 
        {
          "key32": "g"
        }
}

what I have now is something like:

dictionary_1 = {}
dictionary_1["key1"] = {}
dictionary_1["key1"]["key11"] = dictionary["key1"["key11"]
dictionary_1["key3"] = {}
dictionary_1["key3"]["key32"] = dictionary["key3"]["key32"]

which seems like a bad way, because every time the user changes keys he would like extracted, I will need to hardcode it again. There must be some more elegant way to do that.

Edit

For clarification: I have a huge document in one collection in mongodb and the users of my software want to be able to create a new document, with some data from first document and save it in another collection. This is what I came up with. It can be a copy of some elements from one dictionary to another if you please.

Leukonoe
  • 649
  • 2
  • 10
  • 29

1 Answers1

1

You can do it with a dict comprehension:

keys_to_keep = ("key1", "key3")
new_dict = {k:v for k, v in old_dict.items() if k in keys_to_keep}

If you can safely assume that keys never repeat, and that there is only a 2 level nesting, you can try:

keys_to_keep = ("key1", "key12", "key3", "key32")
new_dict = {}


for k, v in dictionary.items():

    if k not in keys_to_keep:
        continue

    if isinstance(v, dict):
        d = {subk: subv for subk, subv in v.items() if subk in keys_to_keep}
        new_dict[k] = d
    else:
        new_dict[k] = v

Note: to handle copies and not references to the original dict, either work on a copy by calling dictionary = old_dict.copy(), or by deep copying with the copy module.

For arbitrary nested dictionaries:

from copy import deepcopy

def prunedict(old_dict, keys_to_keep, copy=False):

    new_dict = {}

    if copy:
        old_dict = deepcopy(old_dict)

    for k, v in old_dict.items():

        if k not in keys_to_keep:
            continue

        if isinstance(v, dict):
            d = prunedict(v, keys_to_keep, copy=False)
            # if d == {}: continue  ## In case you don't want empty entries.
            new_dict[k] = d
        else:
            new_dict[k] = v

    return new_dict
Daniel
  • 11,332
  • 9
  • 44
  • 72
  • Does not give the expected output though – kuro May 23 '17 at 09:25
  • Right, he's got arbitrary nested dicts. Didn't take that into account. I'll see what I come up with – Daniel May 23 '17 at 09:26
  • Also note that the dict has some mutable values. Maybe OP wants a copy. I'd honestly just not answer this question until OP clarifies it. – timgeb May 23 '17 at 09:28
  • Is there anything that still needs clarification? – Leukonoe May 23 '17 at 09:34
  • 1
    Is it possible for you to have subkeys identical to higher level keys? I.e. `dictionary_1["key1"]["key1"]` or `dictionary_1["key3"]["key1"]`. Do you have arbitrary nesting, or only 2-level nesting? – Daniel May 23 '17 at 09:40
  • it is not possible to have subkeys identical to higher level keys. I have arbitrary nesting. – Leukonoe May 23 '17 at 09:50
  • @Leukonoe Please let me know if the solution works for you. – Daniel May 23 '17 at 10:07
  • I really like this solution for arbitrary nested dictionaries. I'm a bit disappointed though that there is no easy way to slice json/dictionary. – Leukonoe May 23 '17 at 10:09
  • You can check out the [`jq`](https://stedolan.github.io/jq/tutorial/) command line tool. Pretty lightweight and has a simple syntax for slicing – Daniel May 23 '17 at 10:14