4

i have a dictionary:

People={
       'name':['john','peter'],
       'age':[56,64]
       }

output

'My name is {name[0]},i am {age[0]} old'.format_map(People)

gives

'My name is john,i am 56 old'

i would like to use format_map in a loop to get:

'My name is {name[x]},i am {age[x]} old'

for each item in the dictionary like:

'My name is john,i am 56 old'
'My name is peter,i am 64 old'

but a loop like:

['My name is {name[x]},i am {age[x]} old'.format_map(People) for x in range(0,len(People['name']))]

gives:

KeyError: 'name'
falsetru
  • 357,413
  • 63
  • 732
  • 636
ukaniddopp
  • 49
  • 1
  • 2

4 Answers4

2

str.format_map accepts a mapping. You cannot pass additional argument.

Alternatively, you can use str.format, but nesting {..} inside indexing ({0[name][{x}]}) is not allowed.

Workaround using str.format and zip / map:

>>> people = {
...    'name': ['john','peter'],
...    'age': [56, 64]
... }

>>> ['My name is {}, i am {} old'.format(*x)
...  for x in zip(people['name'], people['age'])]
['My name is john, i am 56 old', 'My name is peter, i am 64 old']

>>> ['My name is {}, i am {} old'.format(*x)
...  for x in zip(*map(people.get, ['name', 'age']))]
['My name is john, i am 56 old', 'My name is peter, i am 64 old']
falsetru
  • 357,413
  • 63
  • 732
  • 636
2

You may wish to consider using a different structure for your data, something that keeps all the data for each person together. With only two people and with only two attributes for each person, it doesn't really matter, but if you have lots of people and lots of attributes it only takes a single error in one of your lists to destroy the synchronisation. And if that happens, one or more of the people in your database will get the wrong data associated with them.

One possibility is to use a list of dicts, one dict for each person, eg

new_people = [
    {'age': 56, 'name': 'john'},
    {'age': 64, 'name': 'peter'}
]

And then you can easily print it by doing something like this:

for d in new_people:
    print('My name is {0}, I am {1} years old.'.format(d['name'], d['age']))

Output:

My name is john, I am 56 years old.
My name is peter, I am 64 years old.

It's not that hard to convert your existing People dict to a list of dicts. It's probably more readable to do it with a couple of for loops, but I did it with this nested list comprehension / generator expression:

new_people = [dict(t) for t in (((k,v[i]) for k,v in People.items()) for i in range(len(People['name'])))]

PS. It's a convention in Python to use lower-case names for normal variables. Names beginning with an upper-case letter, like People, are usually used for classes. You don't have to follow that convention, but it does make it easier for people who are reading your code. You may notice that in the code you posted above People is in a light blue colour, that's because the SO code formatter thinks its a class name.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • OP uses Python 3.x. There's no `dict.iteritems` in Python 3.x. It should be `items`. – falsetru Sep 21 '14 at 10:25
  • `len(People)` will always return 2 even though there are more people in the lists because there are two keys in the given dictionary; `len(People)` should be `len(People['name'])`. – falsetru Sep 21 '14 at 10:27
  • 1
    I would do: `new_people = [{k: v[i] for k, v in People.items()} for i in range(len(People['name']))]` (using dict-comprehension) – falsetru Sep 21 '14 at 10:33
  • @falsetru: Your dict comprehension is certainly a *lot* cleaner than my (in)comprehension. I really ought to learn Python 3 one of these days... – PM 2Ring Sep 21 '14 at 10:37
  • dictionary comprehension is also available in Python 2.7. – falsetru Sep 21 '14 at 10:38
  • @falsetru But sadly dictionary comprehension is not in Python 2.6.6 – PM 2Ring Sep 21 '14 at 10:47
  • Dictionary comprehension, set comprehension was introduced in Python 2.7, Python 3.1. https://docs.python.org/2/whatsnew/2.7.html#python-3-1-features – falsetru Sep 21 '14 at 10:51
1

You can do two-pass formatting:

people = {
   'name': ['John', 'Peter'],
   'age': [56, 64]
}

for i in range(2):
    'My name is {{name[{0}]}}, I am {{age[{0}]}} years old.'.format(i).format_map(people)

#>>> 'My name is John, I am 56 years old.'
#>>> 'My name is Peter, I am 64 years old.'

...{{...}}... formats to ...{...}..., so is suitable the second call to format.

Veedrac
  • 58,273
  • 15
  • 112
  • 169
0

It might be more intuitive for you to do:

strs = ["My name is " + People['name'][i] + ",i am " + str(People['age'][i]) + " old" for i in len(People)]
print(strs # ['My name is john,i am 56 old', 'My name is peter,i am 64 old'])
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
  • 1
    I would use `len(people['name'])` instead of hard-coded 2 as OP did. BTW, `print` in Python 3.x is a function. – falsetru Sep 21 '14 at 08:05