Dict comprehension, tuples and lazy evaluation - python

I am trying to see if I can pull off something quite lazy in Python.
I have a dict comprehension, where the value is a tuple. I want to be able to create the second entry of the tuple by using the first entry of the tuple.
An example should help.
dictA = {'a': 1, 'b': 3, 'c': 42}
{key: (a = someComplexFunction(value), moreComplexFunction(a)) for key, value in dictA.items()}
Is it possible that the moreComplexFunction uses the calculation in the first entry of the tuple?

You could add a second loop over a one-element tuple:
{key: (a, moreComplexFuntion(a)) for key, value in dictA.items()
for a in (someComplexFunction(value),)}
This gives you access to the output of someComplexFunction(value) in the value expression, but that's rather ugly.
Personally, I'd move to a regular loop in such cases:
dictB = {}
for key, value in dictA.items():
a = someComplexFunction(value)
dictB[key] = (a, moreComplexFunction(a))
and be done with it.

or, you could just write a function to return the tuple:
def kv_tuple(a):
tmp = someComplexFunction(a)
return (a, moreComplexFunction(tmp))
{key:kv_tuple(value) for key, value in dictA.items()}
this also gives you the option to use things like namedtuple to get names for the tuple items, etc. I don't know how much faster/slower this would be though... the regular loop is likely to be faster (fewer function calls)...

Alongside Martijn's answer, using a generator expression and a dict comprehension is also quite semantic and lazy:
dictA = { ... } # Your original dict
partially_computed = ((key, someComplexFunction(value))
for key, value in dictA.items())
dictB = {key: (a, moreComplexFunction(a)) for key, a in partially_computed}

Related

Sort a list of dictionaries by value

I have a list of dictionaries, of the form:
neighbour_list = [{1:4}, {3:5}, {4:9}, {5:2}]
I need to sort the list in order of the dictionary with the largest value. So, for the above code the sorted list would look like:
sorted_list = [{4:9}, {3:5}, {1:4}, {5:2}]
Each dictionary within the list only has one mapping.
Is there an efficient way to do this? Currently I am looping through the list to get the biggest value, then remembering where it was found to return the largest value, but I'm not sure how to extend this to be able to sort the entire list.
Would it just be easier to implement my own dict class?
EDIT: here is my code for returning the dictionary which should come 'first' in an ideally sorted list.
temp = 0
element = 0
for d in list_of_similarities:
for k in d:
if (d[k] > temp):
temp = d[k]
element = k
dictionary = d
first = dictionary[element]
You can use an anonymous function as your sorting key to pull out the dict value (not sure if i've done this the most efficient way though:
sorted(neighbour_list, key = lambda x: tuple(x.values()), reverse=True)
[{4: 9}, {3: 5}, {1: 4}, {5: 2}]
Note we need to coerce x.values() to a tuple, since in Python 3, x.values() is of type "dict_values" which is unorderable. I guess the idea is that a dict is more like a set than a list (hence the curly braces), and there's no (well-) ordering on sets; you can't use the usual lexicographic ordering since there's no notion of "first element", "second element", etc.
You could list.sort using the dict values as the key.
neighbour_list.sort(key=lambda x: x.values(), reverse=1)
Considering you only have one value, for python2 you can just call next on itervalues to get the first and only value:
neighbour_list.sort(key=lambda x: next(x.itervalues()), reverse=1)
print(neighbour_list)
For python3, you cannot call next on dict.values, it would have to be:
neighbour_list.sort(key=lambda x: next(iter(x.values())), reverse=1)
And have to call list on dict.values:
neighbour_list.sort(key=lambda x: list(x.values()), reverse=1)

List comprehensions - extracting values from a dictionary in a dictionary

I'm trying to get a list of names from a dictionary of dictionaries...
list = {'1':{'name':'fred'}, '2':{'name':'john'}}
# this code works a-ok
for key, value in list.items():
names = []
for key, value in list.items():
names.append(value['name'])
# and these consecutive comprehensions also work...
keys = [value for key, value in list.items()]
names = [each['name'] for each in keys]
but how can the last two be combined?
>>> d = {'1':{'name':'fred'}, '2':{'name':'john'}}
You can use the following modification to your list comprehension
>>> [value.get('name') for key, value in d.items()]
['john', 'fred']
Although in this case, you don't need the key for anything so you can just do
>>> [value.get('name') for value in d.values()]
['john', 'fred']
names = [value['name'] for value in list.values()]
names = [value['name'] for key, value in list.items()]
names = [value['name'] for key, value in list.items()]
Since value is defined in the for part of the comprehension, you can perform operations on value for the item part of the comprehension. As noted above, you can simplify this by using list.values() instead.

Tuple from dict of dict in Python

i've a dict of dict in python, for getting a random value from the dict
words = {0:{"wake up": "aaaa"},
1:{"look after": "bbbb"},
2:{"turn on": "cccc"}
}
i want extract the second dict (the values of a number key) from the dict words
(k, v) = words[random.randint(0, 22)]
but the error is thisone
ValueError: need more than 1 value to unpack
why i need another values?
You are accessing the dictionary with a key, which returns just the value:
value = words[random.randint(0, 22)]
but if your keys are all integers, you would be better off using a list instead:
words = [{"wake up": "aaaa"}, {"look after": "bbbb"}, {"turn on": "cccc"}]
value = random.choice(words)
and let random.choice() do all the hard work of picking one at random.
You can still use random.choice() with dictionary values too of course:
value = random.choice(list(words.values()))
would return a random value from the dictionary, and:
key, value = random.choice(list(words.items()))
would return a random (key, value) pair for you.
If you meant to extract the stored dictionary into a key and value, you'd have to extract just one key-value pair from that object; you cannot use tuple unpacking on a dictionary:
>>> key, value = {'foo': 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 1 value to unpack
but this works:
>>> key, value = next(iter({'foo': 'bar'}.items()))
>>> key, value
('foo', 'bar')
but perhaps you should be storing tuples instead of dictionaries if you wanted to extract a random key-value pair, or store the keys and values directly in the words dictionary.
Summary: Method 3 is the most pythonic way to do it.
Without any modification to the data structures, the following code will work for you:
k, v = words[random.randint(0, 2)].items()[0]
However, this is not the best (& pythonic) way to do it.
If your inner dict always has a single key-value pair, then there is really no need to keep it as a dict. Rather, keep the inner data as a tuple.
words = {0: ("wake up", "aaaa"),
1:("look after", "bbbb"),
2:("turn on", "cccc")
}
# Now, the following line should work fine
k, v = words[random.randint(0, 2)]
And If there is no restriction on the outer data structure either, then use list or tuple as follows:
words = (
("wake up", "aaaa"),
("look after", "bbbb"),
("turn on", "cccc")
)
# Now, the random.choice can be used
k, v = random.choice(words)

How to construct nested dictionary comprehension in Python with correct ordering?

I was trying to shorten the code for this problem when I encountered the problem.
Basically, I was trying a nested dictionary comprehension & was unsuccessful in the attempt.
Here is what I tried.
dict2 = {key:value for key, value in line.split(":")
for line in ["1:One", "2:Two", "4:Four"]}
print dict2
When I run this, it gives me
NameError: name 'line' is not defined
And, when I reverse the for statements like this
dict2 = {key:value for line in ["1:One", "2:Two", "4:Four"]
for key, value in line.split(":")}
print dict2
It results in
ValueError: need more than 1 value to unpack
I need help on the nesting structure of dictionary (or list) comprehension. An example would be help a lot.
Note that there is a better way of doing this without a dict comprehension; see below. I’ll first address the issues with your approach.
You need to use nesting order in comprehensions. List your loops in the same order they would be in when nesting a regular loop.
The line.split() expression returns a sequence of two items, but each of those items is not a tuple of a key and a value; instead there is only ever one element being iterated over. Wrap the split in a tuple so you have a 'sequence' of (key, value) items being yielded to assign the two results to two items:
dict2 = {key:value for line in ["1:One", "2:Two", "4:Four"]
for key, value in (line.split(":"),)}
This is the equivalent of:
dict2 = {}
for line in ["1:One", "2:Two", "4:Four"]:
for key, value in (line.split(":"),):
dict2[key] = value
where the nested loop is only needed because you cannot do:
dict2 = {}
for line in ["1:One", "2:Two", "4:Four"]:
key, value = line.split(":")
dict2[key] = value
However, in this case, instead of a dictionary comprehension, you should use the dict() constructor. It wants two-element sequences, simplifying the whole operation:
dict2 = dict(line.split(":") for line in ["1:One", "2:Two", "4:Four"])
You can do this a lot simpler with dict and a generator expression:
>>> lst = ["1:One", "2:Two", "4:Four"]
>>> dict(x.split(":") for x in lst)
{'4': 'Four', '1': 'One', '2': 'Two'}
>>>

Query Python dictionary to get value from tuple

Let's say that I have a Python dictionary, but the values are a tuple:
E.g.
dict = {"Key1": (ValX1, ValY1, ValZ1), "Key2": (ValX2, ValY2, ValZ2),...,"Key99": (ValX99, ValY99, ValY99)}
and I want to retrieve only the third value from the tuple, eg. ValZ1, ValZ2, or ValZ99 from the example above.
I could do so using .iteritems(), for instance as:
for key, val in dict.iteritems():
ValZ = val[2]
however, is there a more direct approach?
Ideally, I'd like to query the dictionary by key and return only the third value in the tuple...
e.g.
dict[Key1] = ValZ1 instead of what I currently get, which is dict[Key1] = (ValX1, ValY1, ValZ1) which is not callable...
Any advice?
Just keep indexing:
>>> D = {"Key1": (1,2,3), "Key2": (4,5,6)}
>>> D["Key2"][2]
6
Use tuple unpacking:
for key, (valX, valY, valZ) in dict.iteritems():
...
Often people use
for key, (_, _, valZ) in dict.iteritems():
...
if they are only interested in one item of the tuple. But this may cause problem if you use the gettext module for multi language applications, as this model sets a global function called _.
As tuples are immutable, you are not able to set only one item like
d[key][0] = x
You have to unpack first:
x, y, z = d[key]
d[key] = x, newy, z
Using a generator expression!
for val in (x[2] for x in dict):
print val
You don't need to use iteritems because you're only looking at the values.

Categories