I have been handling data from graph theory lately. I need to reiterate the shortest path in a form of nested dictionary to a tuple of lists.
For example:
What I received:
{0:{2:{4:{},3:{}}},1:{2:{2:{},7:{}}}}
What I want:
[[4,2,0],[3,2,0],[2,2,1],[7,2,1]]
My first thought was to define a function to append the list. However, I have been struggling from that for several hours already. I have no clue to get into the deepest keys of the dictionary…
Look forward to hearing advice from you guys, really appreciate of your help!!!
(P.s I have the number of layers of the nest, such as 3 for the above example)
A recursive approach works for this. Define a function that will return the path to the nodes in your dictionary like so:
For each item in the dictionary
if the item is not a dictionary, or an empty dictionary, we yield its key.
if the item is a non-empty dictionary, we get the path to the children of this item, and append the item's key to this path before yielding.
def dict_keys_to_list(d):
for k, v in d.items():
if isinstance(v, dict) and v:
for c in dict_keys_to_list(v):
yield c + [k]
else:
yield [k]
To use this:
d = {0:{2:{4:{},3:{}}},1:{2:{2:{},7:{}}}}
x = list(dict_keys_to_list(d))
print(x)
gives:
[[4, 2, 0], [3, 2, 0], [2, 2, 1], [7, 2, 1]]
You can use a recursive generator:
def unnest(d, val=[]):
if d == {}:
yield val
else:
for k,v in d.items():
yield from unnest(v, [k]+val)
list(unnest(d))
Output:
[[4, 2, 0], [3, 2, 0], [2, 2, 1], [7, 2, 1]]
How it works:
for each key, value pair, run a recursive iteration on the sub dictionaries passing the key as parameter.
at each step the new key is added on front of the list
when an empty dictionary is found the path is over, yield the list
Related
I'm new so bear with me.. I could create two separate for loops but I like this method since I'll probably iterate through multiple lists and this is less code. Is it possible in itertools? As I understand it, it creates one list out of the two so I might be out of luck.
import itertools
a = [0, 1, 2]
b = [3, 4, 5]
def finditem(n):
for i in itertools.chain(a, b):
if i == n:
print(n) # here i want (n) and (a or b)
n = 3
finditem(n)
I think what you want is:
given a list of lists and an item to find if it exists in that list
tell me what list that item was found in.
You should probably create a dictionary with your keys being what that list is called and the values for the things in that collection
my_lists = {'a': [0, 1, 2], 'b': [3, 4, 5]}
def find_item(my_lists, item_to_find):
for key in my_lists:
if item_to_find in my_lists[key]:
print(key)
n=3
find_item(my_lists, n)
There isn't much need for itertools here, but you should store your data in a dictionary if you want to be able to give them labels that you can refer to in the program. To list all of the list names containing n:
lists = {'fruits': [0, 1, 2], 'vegetables': [3, 4, 5]}
def find_item(n, lists):
for name, list_ in lists.items():
if n in list_:
print(name)
or similarly:
print([name for name, list_ in lists.items() if n in list_])
or to print only the first list found that contains n:
print(next(name for name, list_ in lists.items() if n in list_))
If you need this for an if condition, and the number of lists is not very large, then probably don't use a loop in the first place, but a simple condition like this:
fruits = [0, 1, 2]
vegetables = [3, 4, 5]
if n in fruits:
# do this
elif n in vegetables:
# do that
I am playing around with dictionaries, and thought how would I create a dictionary using comprehensions. I thought
{k:v for k in [0,1,2] for v in [5,8,7]}
would print as
{0:5, 1:8, 2:7}
But instead it prints as
{0: 7, 1: 7, 2: 7}
Why is this happening and what modifications would I need to make to get the first output?
Your list comprehension is equivalent to nested loops:
result = {}
for v in [5, 8, 7]:
for k in [0, 1, 2]:
result[k] = v
So each iteration of the outer loop sets all the keys to that value, and at the end you have the last value in all of them.
Use zip() to iterate over two lists in parallel.
{k: v for k, v in zip([0, 1, 2], [5, 8, 7])}
You can also just use the dict() constructor:
dict(zip([0, 1, 2], [5, 8, 7]))
Whenever you have trouble with a comprehension, unroll it into the equivalent loops. Which in this case goes:
mydict = {}
for v in [5,8,7]:
for k in [0,1,2]:
mydict[k] = v
Each successive assignment to mydict[k] overwrites the previous one.
I have a dictionary like this:
a = {(8, 9): [[0, 0], [4, 5]], (3, 4): [[1, 2], [6, 7]]}
I would like to subtract the sum of the corresponding elements of the nested lists in the values from each element of each key, and replace the key with the result.
For example:
new_key[0] = 8 - 0+4 = 4, new_key[1] = 9 - (0+5) = 4
Hence the new key becomes (4, 4) and it replaces (8, 9)
I am not able to understand how to access a list of lists which is the value to the key!
Any ideas as to how to do this?
See Access item in a list of lists for indexing list of list.
For your specific case, this should work
b = {(k[0]-v[0][0]-v[1][0], k[1]-v[0][1]-v[1][1]):v for k, v in a.items()}
for key in list(a.keys()):
new_key = []
new_key.append(key[0] - (a[key][0][0] + a[key][1][0]))
new_key.append(key[1] - (a[key][0][1] + a[key][1][1]))
a[new_key] = a.pop(key) # pop() returns the value
Iterate through the dictionary to get the keys and values and create a new one b. Then just point a to the new dictionary b
a = {(8, 9): [[0, 0], [4, 5]], (3, 4): [[1, 2], [6, 7]]}
b = {}
for key, val in a.items():
new_key = (key[0]-(val[0][0]+val[1][0]), key[1]-(val[0][1]+val[1][1]))
b[new_key] = val
a = b
del b
Try This:
b = {}
for key, value in a.items():
new_key = key[0]-(value[0][0]+value[1][0])
new_key_1 = key[1]-(value[0][1]+value[1][1])
u_key = (new_key, new_key_1)
b[u_key]=value
print(b)
The following code will work just as well with any size of key tuple (2, 5, 87, whatever.)
There is no simple way to rename a dictionary key, but you can insert a new key and delete the old one. This isn't recommended for a couple of reasons:
to be safe, you'll need to check to make sure you're not doing something weird like creating a new key only to delete that same key
be careful when you iterate over a dictionary while changing it
If you need a dictionary result, the safest thing is to generate an entirely new dictionary based on a, as has been done here.
The problem you're trying to solve is easier if you transpose the dictionary values.
After calculating the new key, (8, 9): [[0, 0], [4, 5]] should become:
(8 - sum([0, 4]), 9 - sum([0, 5])): [[0, 0], [4, 5]]
Now see how transposing helps:
transposed([[0, 0], [4, 5]]) == [[0, 4], [0, 5]]
then the new key[0] calculation is:
key[0] - sum(transposed(values)[0])
and the new key[1] calculation is:
key[1] - sum(transposed(values)[1])
So transposing makes the calculation easier.
Python dictionaries can't have lists as keys (lists are not hashable) so I've built the key as a list, then converted it to a tuple at the end.
a = {
(8, 9): [[0, 0], [4, 5]],
(3, 4): [[1, 2], [6, 7]]
}
def transpose(m):
return list(zip(*m))
results = {}
for source_keys, source_values in a.items():
transposed_values = transpose(source_values)
key = []
for n, key_item in enumerate(source_keys):
subtractables = sum(transposed_values[n])
key.append(key_item - subtractables)
results[tuple(key)] = source_values
print(results)
>>> python transpose.py
{(4, 4): [[0, 0], [4, 5]], (-4, -5): [[1, 2], [6, 7]]}
The following solution works with three assumptions:
The keys are iterables of integers
The values are iterables of iterables of integers
The inner iterables of each value are the same length as the corresponding key
Practically, this means that the length of the value can be anything, as long as it's a 2D list, and that you can have keys of different lengths, even in the same dictionary, as long as the values match along the inner dimension.
You would want to transpose the values to make the sum easier to compute. The idiom zip(*value) lets you do this quite easily. Then you map sum onto that and subtract the result from the elements of the key.
Another thing to keep in mind is that replacing keys during iteration is a very bad idea. You're better off creating an entirely new dictionary to hold the updated mapping.
from operator import sub
from itertools import starmap
a = {(8, 9): [[0, 0], [4, 5]], (3, 4): [[1, 2], [6, 7]]}
b = {
tuple(starmap(sub, zip(k, map(sum, zip(*v))))): v
for k, v in a.items()
}
The result is
{(4, 4): [[0, 0], [4, 5]], (-4, -5): [[1, 2], [6, 7]]}
Here is an IDEOne Link to play with.
The full version of the loop would look like this:
b = {}
for k, v in a.items():
vt = zip(*v) # transposed values
sums = map(sum, vt) # sum of the 1st elements, 2nd elements, etc
subs = zip(k, sums) # match elements of key with sums to subtract
diffs = starmap(sub, subs) # subtract sums from key
new_key = tuple(diffs) # evaluate the generators
b[new_key] = value
I want to add multiple values to a specific key in a python dictionary. How can I do that?
a = {}
a["abc"] = 1
a["abc"] = 2
This will replace the value of a["abc"] from 1 to 2.
What I want instead is for a["abc"] to have multiple values (both 1 and 2).
Make the value a list, e.g.
a["abc"] = [1, 2, "bob"]
UPDATE:
There are a couple of ways to add values to key, and to create a list if one isn't already there. I'll show one such method in little steps.
key = "somekey"
a.setdefault(key, [])
a[key].append(1)
Results:
>>> a
{'somekey': [1]}
Next, try:
key = "somekey"
a.setdefault(key, [])
a[key].append(2)
Results:
>>> a
{'somekey': [1, 2]}
The magic of setdefault is that it initializes the value for that key if that key is not defined. Now, noting that setdefault returns the value, you can combine these into a single line:
a.setdefault("somekey", []).append("bob")
Results:
>>> a
{'somekey': [1, 2, 'bob']}
You should look at the dict methods, in particular the get() method, and do some experiments to get comfortable with this.
How about
a["abc"] = [1, 2]
This will result in:
>>> a
{'abc': [1, 2]}
Is that what you were looking for?
Append list elements
If the dict values need to be extended by another list, extend() method of lists may be useful.
a = {}
a.setdefault('abc', []).append(1) # {'abc': [1]}
a.setdefault('abc', []).extend([2, 3]) # a is now {'abc': [1, 2, 3]}
This can be especially useful in a loop where values need to be appended or extended depending on datatype.
a = {}
some_key = 'abc'
for v in [1, 2, 3, [2, 4]]:
if isinstance(v, list):
a.setdefault(some_key, []).extend(v)
else:
a.setdefault(some_key, []).append(v)
a
# {'abc': [1, 2, 3, 2, 4]}
Append list elements without duplicates
If there's a dictionary such as a = {'abc': [1, 2, 3]} and it needs to be extended by [2, 4] without duplicates, checking for duplicates (via in operator) should do the trick. The magic of get() method is that a default value can be set (in this case empty set ([])) in case a key doesn't exist in a, so that the membership test doesn't error out.
a = {some_key: [1, 2, 3]}
for v in [2, 4]:
if v not in a.get(some_key, []):
a.setdefault(some_key, []).append(v)
a
# {'abc': [1, 2, 3, 4]}
The list contains other lists:
L = [[3, 3], [4, 2], [3, 2]]
If the first element of the sublist is equal to the first element of other sublists the one that has higher second element has to be remove from the whole list.
So the new list is:
L = [[4,2], [3,2]]
How to do this as efficiently as possible?
L.sort(key=lambda x: x[1], reverse=True)
L = OrderedDict(L).items()
Why that works
If you do a dict(L) with L a list or tuple, this is more or less equivalent to:
{k: v for k, v in L}
As you can see, later values override prior values if duplicate keys (k) are present.
We can make use of this if we are able to put L in the correct order.
In your case, we don't really care about the order of the keys, but we want lower values (i.e. second elements of the sublists) to appear later. This way, any lower value overwrites a higher value with the same key.
It is sufficient to sort by the second elements of the sublists (in reverse order). Since list.sort() is stable this also preserves the original order of the entries as much as possible.
L.sort(key=lambda x: x[1], reverse=True)
collections.OrderedDict(L) now makes the elements unique by first element, keeping insertion order.
The sort() is O(n ln n) and the dict creation adds another O(n). It's possible to do without the sort:
d = OrderedDict()
for k, v in L:
ev = d.get(k, None)
# update value. Always if key is not present or conditionally
# if existing value is larger than current value
d[k] = v if ev is None or ev > v else ev
L = d.items()
But that is a lot more code and probably not at all or not much faster in pure Python.
Edits: (1) make it work with non-integer keys (2) It's enough to sort by second elements, no need for a full sort.
If you don't care about the ordering of the elements in the output list, then you can create a dictionary that maps first items to second items, then construct your result from the smallest values.
from collections import defaultdict
L = [[3, 3], [4, 2], [3, 2]]
d = defaultdict(list)
for k,v in L:
d[k].append(v)
result = [[k, min(v)] for k,v in d.iteritems()]
print result
Result:
[[3, 2], [4, 2]]
This is pretty efficient - O(n) average case, O(n*log(n)) worst case.
You can use this too.
x = [[3, 3], [4, 2], [3, 2]]
for i in x:
if i[0]==i[1]:
x.pop(x.index(i))