2

How can I use dictionary comprehension for values that are literals or list

Right now I'm able to iterate through a nested dictionary and get as a result dict with nested values, but I would like to include in the output dict, list, and literals (int, str)

Here is my example ( I know that isinstance is not needed here)

nested_dict = {'first':{'a':1}, 'second':{'b':2}, 'third': 3, 'fourth': [1, 2, 3, 4]}

float_dict = {
    outer_k: { float(inner_v)
        for (inner_k, inner_v) in outer_v.items()}
            for (outer_k, outer_v) in nested_dict.items()
                if isinstance(outer_v, dict) 
}

print(float_dict)

Expected output:

{'first': {'a': 1.0}, 'second': {'b': 2.0}, 'third': 3.0, 'fourth': [1.0, 2.0, 3.0, 4.0]}
U13-Forward
  • 69,221
  • 14
  • 89
  • 114
Andrew
  • 1,507
  • 1
  • 22
  • 42

3 Answers3

7

It's not (reasonably) possible using a single comprehension, you want a recursive function like this:

def floatify(v):
    if isinstance(v, list):
        return list(map(floatify, v))
    if isinstance(v, dict):
        return {k: floatify(_v) for k, _v in v.items()}
    return float(v)
>>> floatify(nested_dict)
{'first': {'a': 1.0}, 'second': {'b': 2.0}, 'third': 3.0, 'fourth': [1.0, 2.0, 3.0, 4.0]}

Note that you can make this function even more generic:

def anyify(v, f):
    if isinstance(v, list):
        return [anyify(_v, f) for _v in v]
    if isinstance(v, dict):
        return {k: anyify(_v, f) for k, _v in v.items()}
    return f(v)

anyify(nested_dict, float)
deceze
  • 510,633
  • 85
  • 743
  • 889
  • @deceze Really nice. Thank you! One more question for a kind of similar task, if I need to process key instead of value, for example, i need to lowercase all dict keys in case they are uppercased, now most probably I will not be able to access nested dict if `key` will be lowered – Andrew Sep 15 '21 at 11:37
  • @Andrew Basically the same thing, but you'd process the `k` in the dict comprehension. I'm sure you can figure it out with a bit of work. – deceze Sep 15 '21 at 11:44
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/237139/discussion-on-answer-by-deceze-how-can-i-use-dictionary-comprehensions-where-nes). – deceze Sep 15 '21 at 15:12
2

Or without recursion you could kinda make it in one-liner:

{outer_k: ({inner_k: float(inner_v) for (inner_k, inner_v) in outer_v.items()} if isinstance(outer_v, dict) else ([float(i) for i in outer_v] if isinstance(outer_v, list) else float(outer_v))) for (outer_k, outer_v) in nested_dict.items()}

Ex:

nested_dict = {'first':{'a':1}, 'second':{'b':2}, 'third': 3, 'fourth': [1, 2, 3, 4]}

float_dict = {outer_k: ({inner_k: float(inner_v) for (inner_k, inner_v) in outer_v.items()} if isinstance(outer_v, dict) else ([float(i) for i in outer_v] if isinstance(outer_v, list) else float(outer_v))) for (outer_k, outer_v) in nested_dict.items()}
print(float_dict)

Output:

{'first': {'a': 1.0}, 'second': {'b': 2.0}, 'third': 3.0, 'fourth': [1.0, 2.0, 3.0, 4.0]}
U13-Forward
  • 69,221
  • 14
  • 89
  • 114
1

Here is a solution that uses a queue. By taking advantage of the mutability of dicts and lists, we can enqueue those objects (even inner ones and even how deeply nested) and still be able to update the source data. This will traverse and enqueue each inner element of the data. If the element is an int, it will convert it to float.

import copy

nested_dict = {'first':{'a':1}, 'second':{'b':2}, 'third': 3, 'fourth': [4, {"fifth": 5}, 6, [{"sixth": [7, 8]}, 9], 10]}

float_dict = copy.deepcopy(nested_dict)

queue = [float_dict]
while queue:
    data = queue.pop()
    items = data.items() if isinstance(data, dict) else enumerate(data)
    for key, value in items:
        if isinstance(value, int):
            data[key] = float(value)
        elif isinstance(value, (dict, list)):
            queue.append(value)

print(float_dict)

Output

{'first': {'a': 1.0}, 'second': {'b': 2.0}, 'third': 3.0, 'fourth': [4.0, {'fifth': 5.0}, 6.0, [{'sixth': [7.0, 8.0]}, 9.0], 10.0]}
  • This is exactly *not* recursive. You've unrolled the recursion into a loop. Which may be beneficial for very very *very* deeply nested data structures… – deceze Sep 15 '21 at 10:45