What is the "most pythonic" way to build a dictionary where I have the values in a sequence and each key will be a function of its value? I'm currently using the following, but I feel like I'm just missing a cleaner way. NOTE: values is a list that is not related to any dictionary.
for value in values:
new_dict[key_from_value(value)] = value
At least it's shorter:
dict((key_from_value(value), value) for value in values)
>>> l = [ 1, 2, 3, 4 ]
>>> dict( ( v, v**2 ) for v in l )
{1: 1, 2: 4, 3: 9, 4: 16}
In Python 3.0 you can use a "dict comprehension" which is basically a shorthand for the above:
{ v : v**2 for v in l }
Py3K:
{ key_for_value(value) : value for value in values }
This method avoids the list comprehension syntax:
dict(zip(map(key_from_value, values), values))
I will never claim to be an authority on "Pythonic", but this way feels like a good way.
Related
I've got two dictionaries of unequal length, e.g.:
people = {"john" : "carpenter", "jill": "locksmith", "bob":"carpenter", "jane": "pilot", "dan": "locksmith"}
jobcode = {"carpenter": 1, "locksmith": 2, "pilot": 3}
What I'm wanting to do is replace the values in people with the jobcode value.
So youd end up with:
n
people = {"john": 1, "jill": 2, "bob": 1, "jane": 3, "dan":2}
I'd be happy to make another new dict that encapsulates this new data as well but so far the closest I think I've come is this... I think...
Any help would be greatly appreciated.
You can easily achieve this with dict comprehension
{k: jobcode[v] for k, v in people.items()}
However you should be careful since it can raise KeyError.
Another way with default jobcode with dict .get() method:
default_jobcode = 1000
final_dict = {k: jobcode.get(v, default_jobcode) for k, v in people.items()}
UPDATE
As #Graipher kindly noted, if jobcode dict is lack of key-value pair, you can leave item untouched as such:
final_dict = {k: jobcode.get(v, v) for k, v in people.items()}
Which is probably better solution that having default jobcode.
Ty this:-
people = {'john' : 'carpenter', 'jill': 'locksmith', 'bob':'carpenter', 'jane': 'pilot', 'dan': 'locksmith'}
jobcode = {'carpenter': 1, 'locksmith': 2, 'pilot': 3}
for i,j in people.items():
if j in jobcode.keys():
people[i] = jobcode[j]
print(people)
Let say we have a map-dict and data-dict :
m = { 1:2, 4:5, 3:7 }
data = { 1:7, 4:1, 3:6 }
we should replace all occurrences both key&val in "data" according to "m" :
data1 = { 2:7, 5:2, 7:6 }
what is the shortest, fast way to do this ?
I was thinking of converting data to list do the replacements and convert back to dict. But even that gets too involved.
Converting list to dict is OK :
dict(zip(lst[0::2], lst[1::2]))
Converting dict to list is :
data.items()
but this one does not return list, but list of tuples, which make things too involved i.e we need to additionally flatten this LoT.
I was wondering if there is a better way, with emphasis on speed/mem.
dict comprehension:
data1 = {m.get(k, k): m.get(v, v) for k, v in data.items()}
Note that {4: 1, 4: 6} in your example is a single element.
index = {
u'when_air': 0,
u'chrono': 1,
u'age_marker': 2,
u'name': 3
}
How can I make this more beautiful (and clear) way than just manually setting each value?
like:
index = dict_from_range(
[u'when_air', u'chrono', u'age_marker', u'name'],
range(4)
)
You can feed the results of zip() to the builtin dict():
>>> names = [u'when_air', u'chrono', u'age_marker', u'name']
>>> print(dict(zip(names, range(4))))
{'chrono': 1, 'name': 3, 'age_marker': 2, 'when_air': 0}
zip() will return a list of tuples, where each tuple is the ith element from names and range(4). dict() knows how to create a dictionary from that.
Notice that if you give sequences of uneven lengths to zip(), the results are truncated. Thus it might be smart to use range(len(names)) as the argument, to guarantee an equal length.
>>> print(dict(zip(names, range(len(names)))))
{'chrono': 1, 'name': 3, 'age_marker': 2, 'when_air': 0}
You can use a dict comprehension together with the built-in function enumerate to build the dictionary from the keys in the desired order.
Example:
keys = [u'when_air', u'chrono', u'age_marker', u'name']
d = {k: i for i,k in enumerate(keys)}
print d
The output is:
{u'age_marker': 2, u'when_air': 0, u'name': 3, u'chrono': 1}
Note that with Python 3.4 the enum module was added. It may provide the desired semantics more conveniently than a dictionary.
For reference:
http://legacy.python.org/dev/peps/pep-0274/
https://docs.python.org/2/library/functions.html#enumerate
https://docs.python.org/3/library/enum.html
index = {k:v for k,v in zip(['when_air','chrono','age_marker','name'],range(4))}
This?
#keys = [u'when_air', u'chrono', u'age_marker', u'name']
from itertools import count
print dict(zip(keys, count()))
I was wondering how do you mix list and dict together on Python? I know on PHP, I can do something like this:
$options = array(
"option1",
"option2",
"option3" => array("meta1", "meta2", "meta3"),
"option4"
);
The problem is python have different bracket for different list. () for tuple, [] for list, and {} for dict. There don't seems to be any way to mix them and I'm keep getting syntax errors.
I am using python 2.7 now. Please advice how to do it correctly.
Much thanks,
Rufas
Update 1:
I'll slightly elaborate what I'm trying to do. I am trying to write a simple python script to do some API requests here:
http://www.diffbot.com/products/automatic/article/
The relevant part is the fields query parameters. It is something like ...&fields=meta,querystring,images(url,caption)... . So the above array can be written as (in PHP)
$fields = array(
'meta',
'querystring',
'images' => array('url', 'caption')
);
And the $fields will be passed to a method for processing. The result will be returned, like this:
$json = diffbot->get("article", $url, $fields);
The thing is - I have no problem in writing it in PHP, but when I try to write it in Python, the thing is not as easy as it seems...
You can do it this way:
options = {
"option1": None,
"option2": None,
"option3": ["meta1", "meta2", "meta3"],
"option4": None,
}
But options is a dictionary in this case.
If you need the order in the dictionary you can use OrderedDict.
How can you use OrderedDict?
from collections import OrderedDict
options = OrderedDict([
("option1", None),
("option2", None),
("option3", ["meta1", "meta2", "meta3"]),
("option4", None),
])
print options["option3"]
print options.items()[2][1]
print options.items()[3][1]
Output:
['meta1', 'meta2', 'meta3']
['meta1', 'meta2', 'meta3']
None
Here you can access options either using keys (like option3), or indexes (like 2 and 3).
Disclaimer. I must stress that this solution is not one-to-one mapping between PHP and Python. PHP is another language, with other data structures/other semantics etc. You can't do one to one mapping between data structures of Python and PHP. Please also consider the answer of Hyperboreus (I gave +1 to him). It show another way to mix lists and dictionaries in Python. Please also read our discussion below.
Update1.
How can you process such structures?
You must check which type a value in each case has.
If it is a list (type(v) == type([])) you can join it;
otherwise you can use it as it is.
Here I convert the structure to a URL-like string:
options = {
"option1": None,
"option2": None,
"option3": ["meta1", "meta2", "meta3"],
"option4": "str1",
}
res = []
for (k,v) in options.items():
if v is None:
continue
if type(v) == type([]):
res.append("%s=%s" % (k,"+".join(v)))
else:
res.append("%s=%s" % (k,v))
print "&".join(res)
Output:
option4=str1&option3=meta1+meta2+meta3
This seems to do the same thing:
options = {0: 'option1',
1: 'option2',
2: 'option4'
'option3': ['meta1', 'meta2', 'meta3'] }
More in general:
[] denote lists, i.e. ordered collections: [1, 2, 3] or [x ** 2 for x in [1, 2, 3]]
{} denote sets, i.e. unordered collections of unique (hashable) elements, and dictionaries, i.e. mappings between unique (hashable) keys and values: {1, 2, 3}, {'a': 1, 'b': 2}, {x: x ** 2 for x in [1, 2, 3]}
() denote (among other things) tuples, i.e. immutable ordered collections: (1, 2, 3)
() also denote generators: (x ** 2 for x in (1, 2, 3))
You can mix them any way you like (as long as elements of a set and keys of a dictionary are hashable):
>>> a = {(1,2): [2,2], 2: {1: 2}}
>>> a
{(1, 2): [2, 2], 2: {1: 2}}
>>> a[1,2]
[2, 2]
>>> a[1,2][0]
2
>>> a[2]
{1: 2}
>>> a[2][1]
2
I'm pretty sure there are 3 answers for my question and while it received a -1 vote, it is the closest to what I want. It is very strange now that it is gone when I want to pick that one up as "accepted answer" :(
To recap, the removed answer suggest I should do this:
options = [
"option1",
"option2",
{"option3":["meta1", "meta2", "meta3"]},
"option4"
]
And that fits nicely how I want to process each item on the list. I just loop through all values and check for its type. If it is a string, process it like normal. But when it is a dict/list, it will be handled differently.
Ultimately, I managed to make it work and I get what I want.
Special thanks to Igor Chubin and Hyperboreus for providing suggestions and ideas for me to test and discover the answer I've been looking for. Greatly appreciated.
Thank you!
Rufas
I need a structure for my little Python program to save a list of max 500 names with one number each. The names would be unique, but the numbers would repeat (often). I first thought of a dictionary, but I also need to be able to search for the numbers, for instance I would need to change all 2 to 3. What would you recommend?
I am new to Python, so I am sure I overlooked a simple solution.
("Spiderman",1)
("Dr. House",2)
("Jon Skeet",1)
You can use a dict, and search by value like so:
names = {"Spiderman":1, "Dr. House":2, "Jon Skeet":1}
resulting_keys = [k for k, v in names.iteritems() if v == 1]
# returns ['Spiderman', 'Jon Skeet']
Then you can do something like:
names.update(dict((k,names[k] + 1) for k in resulting_keys))
Which will now update names to:
{'Jon Skeet': 2, 'Dr. House': 2, 'Spiderman': 2}
Dictionary would work. If you need to change the stored values:
>>> d = {"SpiderMan":1, "Dr.House":2, "Jon Skeet":1}
>>> for k,v in d.items():
... if v == 1:
... d[k] = v+1
...
...
...
>>> d
{'SpiderMan': 2, 'Dr.House': 2, 'Jon Skeet': 2}
It's going to be a linear search (O(n)). Do you need better than that?