0

I would like to print a nested dictionary in a certain format (see the expected output below) using .format().

It should be:

  • sorted alphabetically (by name, which I managed to do)
  • the column width should be the width of the longest item plus 3 spaces (which I didn't manage to do)

The dictionary in question:

data = {"Kevin H": {"street":"Maple Street 13", "phone": "01234567"},
           "Stewart Bob": {"street":"Agave Street 124", "phone": "76543210"}, 
           "John Paul": {"street":"Old Town Road 1", "phone": "09876543"}}

Expected output:

John Paul     Old Town Road 1    09876543   
Kevin H       Maple Street 13    01234567   
Stewart Bob   Agave Street 124   76543210   

So far I have:

sorted_data = dict(sorted(data.items()))

for name, info in sorted_data.items():
        print("{} {} {}".format(name, info["street"],info["phone"]))

So, my question: how do I make the width of each column the length of the longest string (in that column) plus 3 spaces?

2 Answers2

2

Here's a solution using a functional approach.

  1. Get the columns you're interested in:
>>> columns = sorted((k, v['street'], v['phone']) for k, v in contact.items())
>>> columns
[('John Paul', 'Old Town Road 1', '09876543'),
 ('Kevin H', 'Maple Street 13', '01234567'),
 ('Stewart Bob', 'Agave Street 124', '76543210')]
  1. Get the length of each string:
>>> lengths = map(lambda x: list(map(len, x)), columns)
>>> list(lengths)
[[7, 15, 8], [11, 16, 8], [9, 15, 8]]
  1. Get the max value in each column. It's convenient to transpose the array and get the max value for each row:
>>> lengths = map(list, zip(*lengths))
>>> max_lengths = map(max, lengths)
>>> list(max_lengths)
[11, 16, 8]
  1. Print the data using a variable within the format specifier to indicate the field widths:
>>> PAD = 3
>>> max_lengths = [x+PAD for x in max_lengths]
>>> for row in columns:
        print("{:{name_w}} {:{street_w}} {:{code_w}}".format(*row, 
            name_w=max_lengths[0], street_w=max_lengths[1], code_w=max_lengths[2]))
John Paul      Old Town Road 1     09876543
Kevin H        Maple Street 13     01234567
Stewart Bob    Agave Street 124    76543210
Woodford
  • 3,746
  • 1
  • 15
  • 29
1

You have to somehow get the maximal width of each column before looping over the items, like so:

contacts = {
    "Kevin H": {"street":"Maple Street 13", "phone": "01234567"},
    "Stewart Bob": {"street":"Agave Street 124", "phone": "76543210"},
    "John Paul": {"street":"Old Town Road 1", "phone": "09876543"}
}

# Collect all widths for each columns
name_widths = [len(x) for x in contacts]
street_widths = [len(contacts[x]['street']) for x in contacts]
phone_widths = [len(contacts[x]['phone']) for x in contacts]

# Sort contacts
sorted_contacts = dict(sorted(contacts.items()))

for name, info in sorted_contacts.items():
    print('{} {} {}'.format(
        name.ljust(max(name_widths) + 3),
        info['street'].ljust(max(street_widths) + 3),
        info['phone'].ljust(max(phone_widths) + 3)
    ))

After you've done that, it's just a matter of using the built-in string-methods for justification.