2

I have a dict which contains some lists and some dicts, as illustrated below.

What is the most pythonic way to iterate over the dict and print out the name and address pairs for each top level dict key?

Thanks

{
    'Resent-Bcc': [],
    'Delivered-To': [],
    'From': {'Name': 'Steve Watson', 'Address': 'steve.watson@example.org'},
    'Cc': [],
    'Resent-Cc': [],
    'Bcc': [ {'Name': 'Daryl Hurstbridge', 'Address': 'daryl.hurstbridge@example.org'},
             {'Name': 'Sally Hervorth', 'Address': 'sally.hervorth@example.org'},
             {'Name': 'Mike Merry', 'Address': 'mike.merry@example.org'},
             {'Name': 'Jenny Callisto', 'Address': 'jenny.callisto@example.org'}
           ],
    'To': {'Name': 'Darius Jedburgh', 'Address': 'darius.jedburgh@example.org'}
}
Duke Dougal
  • 24,359
  • 31
  • 91
  • 123

5 Answers5

3

Use the iteritems() method on the dict. It's clear and easy to understand: that seems Pythonic to me. iteritems() also creates less temporary items than items(), as Preet Kukreti mentioned in the comments. First, fix your data. Right now, some of the values in the top-level dict are lists, and some are more dicts:

# list
'Delivered-To': [],
# dict
'From': {'Name': 'Steve Watson', 'Address': 'steve.watson@example.org'},

This means you have to check the type of the value and act accordingly (and you might forget to check!). Make your data consistent:

# list
'Delivered-To': [],
# also list
'From': [{'Name': 'Steve Watson', 'Address': 'steve.watson@example.org'}],

This will prevent weird type-related bugs in the future. Since Python is an interpreted language, it's very easy to make type bugs and not notice until your code is in production and crashes. Try to make your code as type-safe as possible!

Then you can use something like this:

for k, v in d.iteritems():
  for row in v:
    if "Name" in row and "Address" in row:
      print row["Name"], ":", row["Address"]
Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
Vlad the Impala
  • 15,572
  • 16
  • 81
  • 124
  • Upvoted but prefer `.iteritems()`. It is more efficient than `.items()`, because `items()` creates more temporaries. – Preet Kukreti Apr 03 '12 at 02:21
  • 1
    You have sort of answered my question. I was trying to understand if there was a smooth way of dealing with the fact that the top level dict contains a mix of lists and dicts. You appear to be suggesting that there is not, and that the top level array should be changed to be more consistent. I was hoping to avoid this. – Duke Dougal Apr 03 '12 at 02:25
  • @DukeDougal the smoothest way is what gnibbler posted: check the type of the object and act accordingly. – Vlad the Impala Apr 03 '12 at 02:34
  • @DukeDougal yes the best way is to actually write a generator function that returns a generator object (which is iterable), which gives a homogenous "view" of the data without creating local copies. This is done internally via `type()` or `isinstance` and then converted and `yield`ed as the target homogenous type (e.g. `dict` or `list`), so you can iterate over a homogenous "view" of a non-homogenous collection. You could also do this via the tuple/generator variant of list comprehension `((..))` or a lambda function. – Preet Kukreti Apr 03 '12 at 02:34
  • @preetKukreti any chance of a code fragment to illustrate your suggestion? thanks – Duke Dougal Apr 03 '12 at 11:08
  • @DukeDougal in fact what gnibbler has done is basically what I described (generator variant of list comprehension) – Preet Kukreti Apr 03 '12 at 11:18
2

One way is to change the lone dicts into a list containing the dict. Then all the entries can be treated the same

>>> D = {
...     'Resent-Bcc': [],
...     'Delivered-To': [],
...     'From': {'Name': 'Steve Watson', 'Address': 'steve.watson@example.org'},
...     'Cc': [],
...     'Resent-Cc': [],
...     'Bcc': [ {'Name': 'Daryl Hurstbridge', 'Address': 'daryl.hurstbridge@example.org'},
...              {'Name': 'Sally Hervorth', 'Address': 'sally.hervorth@example.org'},
...              {'Name': 'Mike Merry', 'Address': 'mike.merry@example.org'},
...              {'Name': 'Jenny Callisto', 'Address': 'jenny.callisto@example.org'}
...            ],
...     'To': {'Name': 'Darius Jedburgh', 'Address': 'darius.jedburgh@example.org'}
... }
>>> L = [v if type(v) is list else [v] for v in D.values()]
>>> [(d["Name"], d["Address"]) for item in L for d in item ]
[('Steve Watson', 'steve.watson@example.org'), ('Daryl Hurstbridge', 'daryl.hurstbridge@example.org'), ('Sally Hervorth', 'sally.hervorth@example.org'), ('Mike Merry', 'mike.merry@example.org'), ('Jenny Callisto', 'jenny.callisto@example.org'), ('Darius Jedburgh', 'darius.jedburgh@example.org')]

Or the one liner version

[(d["Name"], d["Address"]) for item in (v if type(v) is list else [v] for v in D.values())]
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • I think you should use `isinstance` instead of `type` and `is`. – krousey Apr 03 '12 at 02:18
  • @Kris, isinstance is appropriate when the object may be a subclass of list. I think that is unlikely in this case – John La Rooy Apr 03 '12 at 02:22
  • @gnibbler do you think that `isinstance` is inappropriate when the object is exactly a list and not a subclass? (For example, I could easily see tuples here…) – kojiro Apr 03 '12 at 02:28
  • @gnibbler `isinstance` is always better than equality checks on `type`. The cases where `isinstance` will work is a superset of the cases `type` equality checking will work. Not that either technique is good, but `isinstance` is "less bad" and should be preferred. – Preet Kukreti Apr 03 '12 at 02:38
  • @kojiro, that's a different problem because tuple's are a sequence but not a subclass of list. So you could say `isinstance(v, (list, tuple))`, but that's just unnecessarily adding complication if you know it will always be a list. – John La Rooy Apr 03 '12 at 02:41
  • Whatever happened to duck typing? – kojiro Apr 03 '12 at 03:21
  • @kojiro, ducktyping in this case is tricky since both lists and dicts are iterable. You could however check for a dictish object by using `getattr(v, get)`, for example, but I don't think that makes the code any clearer to read. `v if type(v) is list else [v]` clearly expresses the idea - if `v` isn't already a list, stick it inside one – John La Rooy Apr 03 '12 at 03:25
1

It's probably best to keep your data simple, by making the naked dict's be a list of one element holding the original dict. Otherwise, you're kind of asking for harder to test code.

I tend to lean away from isinstance(foo, dict) and instead use things like: if getattr(d, 'iteritems'): print list(d.iteritems())

...It strikes me as more duck-typed this way; it opens the door to using one of the many dict-replacements - things that act like a dict, but nominally aren't a dict.

dstromberg
  • 6,954
  • 1
  • 26
  • 27
0
for key in header:
    if header[key] and type(header[key])==type([]):
        for item in header[key]:
            print (item)
    elif type(header[key])==type({}):
        print(header[key])

# this option is not the easiest to read, so I classify it as less "pythonic"       
l = [header[key] for key in header if header[key] and type(header[key])==type({})] + [header[key][i] for key in header if header[key] and type(header[key])==type([]) for i in range(len(header[key]))]
for item in l:
    print(item)

if you're looking for the contents of a specific header you could modify the if statements accordingly. Both of these examples print the dictionaries, but could easily be adapted to print specific values.

zinzendorf
  • 120
  • 6
-1
for i in dict:
   if 'Name' in dict[i]: 
      print (dict[i]['Name'],dict[i]['Address'])

this will not work for the bcc where its in a list (right now it will only print the from and to names and addresses) Do you need it to print the bcc addresses too?

apple16
  • 1,137
  • 10
  • 13