0

Lets say I have a dictionary like this:

myDict = {
    1: {
        "a": "something",
        "b": [0, 1, 2],
        "c": ["a", "b", "c"]
    },
    2: {
        "a": "somethingElse",
        "b": [3, 4, 5],
        "c": ["d", "e", "f"]
    },
    3: {
        "a": "another",
        "b": [6, 7, 8],
        "c": ["g", "h", "i"]
    }
}

And this is my code:

for id, obj in myDict.items():
    for key, val in obj.items():
        if key is "b":
            for item in val:
                # apply some function to item

Is there a better way to iterate over a list inside a nested dict? Or is there a pythonic way to do this?

Mico
  • 413
  • 1
  • 6
  • 15
  • `if key is "b"`, but what about `"c"`? They are lists too. You don't want them? – gil Feb 12 '16 at 04:35
  • @gill I don't need "c". I only need values from "b" because I need to apply some function for each value inside the list. – Mico Feb 12 '16 at 04:39

5 Answers5

2

You absolutely do not need to iterate the list to print it (unless this is a functional requirement for the code you are writing).

Very simply, you could do this:

for id, obj in myDict.items():
    if "b" in obj:
        print obj["b"]

To map the list object, represented by obj['b'] to another function, you can use the map function:

map(foo, obj["b"])   
David Zemens
  • 53,033
  • 11
  • 81
  • 130
1

If you're dictionary is always two levels deep, I don't see anything wrong with your approach. In your implementation, I would use key == "b" rather than key is "b". Using is will test for identity (e.g. id(a) == id(b)), while == will test for equality (e.g. a.__eq__(b)). This functions the same way when I test it in IDLE, but it's not a good habit to get into. There's more info on it here: How is the 'is' keyword implemented in Python?

If you want to deal with varying level dictionaries, you could use something like:

def test_dict_for_key(dictionary, key, function):
    for test_key, value in dictionary.items():
        if key == test_key:
            dictionary[key] = type(value)(map(function, value))
        if isinstance(value, dict):
            test_dict_for_key(value, key, function)

An example usage might be something like:

myDict = {
    1: {
        "a": "something",
        "b": [0, 1, 2],
        "c": ["a", "b", "c"]
    },
    2: {
        "a": "somethingElse",
        "b": [3, 4, 5],
        "c": ["d", "e", "f"]
    },
    3: {
        "a": "another",
        "b": [6, 7, 8],
        "c": ["g", "h", "i"]
    }
}

# adds 1 to every entry in each b
test_dict_for_key(myDict, "b", lambda x: x + 1)

# prints [1, 2, 3]
print(myDict[1]["b"])
Jared Goguen
  • 8,772
  • 2
  • 18
  • 36
1

I'm a fan of generator expressions.

inner_lists = (inner_dict['b'] for inner_dict in myDict.values())
# if 'b' is not guaranteed to exist,
# replace inner_dict['b'] with inner_dict.get('b', [])
items = (item for ls in inner_lists for item in ls)

Now you can either use a foo loop

for item in items:
    # apply function

or map

transformed_items = map(func, items)
gil
  • 2,086
  • 12
  • 13
0

A couple fixes could be made.

  1. Don't use is when comparing two strings (if key is "b":)
  2. Simply say print(item) instead of using .format(), since you only have one variable that you're printing, with no additional string formatting

Revised code:

for id, obj in myDict.items():
    for key, val in obj.items():
        if key == "b":
            for item in val:
                print(item)
A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76
0

If you are sure that you will have a b key in every case, you can simply do:

for id, obj in myDict.items():
    for item in obj["b"]:
        print item
Selcuk
  • 57,004
  • 12
  • 102
  • 110