Convert nested dictionary into list of tuples - python

I have a nested dictionary that looks like the following
db1 = {
'Diane': {'Laundry': 2, 'Cleaning': 4, 'Gardening': 3},
'Betty': {'Gardening': 2, 'Tutoring': 1, 'Cleaning': 3},
'Charles': {'Plumbing': 2, 'Cleaning': 5},
'Adam': {'Cleaning': 4, 'Tutoring': 2, 'Baking': 1},
}
The desired sorted dictionary looks like the following
[(5, [('Cleaning', ['Charles'])]),
(4, [('Cleaning', ['Adam', 'Diane'])]),
(3, [('Cleaning', ['Betty']), ('Gardening', ['Diane'])]),
(2, [('Gardening', ['Betty']), ('Laundry', ['Diane']),
('Plumbing', ['Charles']), ('Tutoring', ['Adam'])]),
(1, [('Baking', ['Adam']), ('Tutoring', ['Betty'])])]
it is a list of 2 tuples, the first index is the skill level sorted by decreasing level and the second index is another list of 2 tuple which contains their names as another list inside it. Do I need to extract info from the original dictionary and build a completely new list of tuples? Or I just need to simply change original dictionary into a list of 2 tuple

You can build up an intermediate dictionary, and then use it to produce your final output, as follows:
from pprint import pprint
db1 = {
'Diane': {'Laundry': 2, 'Cleaning': 4, 'Gardening': 3},
'Betty': {'Gardening': 2, 'Tutoring': 1, 'Cleaning': 3},
'Charles': {'Plumbing': 2, 'Cleaning': 5},
'Adam': {'Cleaning': 4, 'Tutoring': 2, 'Baking': 1},
}
d = {}
for k, v in db1.items():
for kk, vv in v.items():
if vv not in d:
d[vv] = {}
if kk not in d[vv]:
d[vv][kk] = []
d[vv][kk].append(k)
out = sorted([(k,
[(kk, sorted(v[kk])) for kk in sorted(v.keys())])
for k, v in d.items()],
key=lambda t:t[0],
reverse=True)
pprint(out)
Gives:
[(5, [('Cleaning', ['Charles'])]),
(4, [('Cleaning', ['Adam', 'Diane'])]),
(3, [('Cleaning', ['Betty']), ('Gardening', ['Diane'])]),
(2,
[('Gardening', ['Betty']),
('Laundry', ['Diane']),
('Plumbing', ['Charles']),
('Tutoring', ['Adam'])]),
(1, [('Baking', ['Adam']), ('Tutoring', ['Betty'])])]
(Note: it might be possible to use some kind of nested defaultdict to avoid the two if statements shown here, but I have not attempted this. If you did d=defaultdict(dict), that would avoid the first if statement, but the second would remain.)

Related

searching for a list in a list of ordered dictionaries

So I have a list of ordered dictionaries which all have 'name' keys which take a string as a value and then a series of other keys which take integers as values. I also have a list of integers separate from the list of ordered dictionaries. I would like to search through the list of ordered dictionaries and see if there are any dictionaries which have all the integers in the list, and if so, what the 'name' value in that list is. Is there any way to do this?
I.e. i have a list of dictionaries with dictionaries like so:
dict = OrderedDict({('name' : 'John'), ('int1': 5), ('int2': 3), ('int3': 1)}), OrderedDict({('name': 'Jack'), ('int1': 1), ('int2': 6), ('int3': 7)})
and then a list of integers like: list = [3, 2, 5]
and if there is a match between the list and the integers in any of the ordered dictionaries, I would like to get the name returned (so in the above case, John).
This may be very basic in which case I apologise, I'm very new to python and coding in general. I've been searching for hours but I haven't found anything I can understand.
If I understand your question right (not that the example data, or the result for John is correct), you may be looking for
dicts = [
{"name": "John", "int1": 5, "int2": 3, "int3": 1},
{"name": "Jack", "int1": 1, "int2": 6, "int3": 7},
{"name": "Mallory", "int1": 1, "int2": 6, "int3": 3},
]
def find_with_keyset(dicts, keyset):
for dict in dicts:
if all(
key in dict and dict[key] == value
for (key, value) in keyset.items()
):
yield dict
def find_first_with_keyset(dicts, keyset):
for result in find_with_keyset(dicts, keyset):
return result
for match in find_with_keyset(dicts, {"int2": 6}):
print(match["name"])
print("---")
print(find_first_with_keyset(dicts, {"int1": 5, "int2": 3, "int3": 1}))
This prints out
Jack
Mallory
---
{'name': 'John', 'int1': 5, 'int2': 3, 'int3': 1}
The idea is that the find_with_keyset generator function filters a given iterable of dicts based on a key subset; for ease-of-use there's find_first_with_keyset which will return the first match, or None.
To turn [1, 2, 3] to {'int1': 1, ...} you can use e.g. {f'key{i}': value for (i, value) in enumerate([1, 2, 3], 1)}.

Python: Lists to Dictionary

I'm writing this question despite the many answers on stackoverflow as the solutions did not work for my problem.
I have 2 Lists, List1 and List2. When I dict(zip(List1,List2)) the order of the elements inside the dictionary are disturbed.
print s_key
print value
sorted_dict = {k: v for k,v in zip(s_key,value)}
another_test = dict(zip(s_key,value))
print sorted_dict
print another_test
print zip(s_key,value))
Terminal :
[2, 1, 3]
[31, 12, 5]
{1: 12, 2: 31, 3: 5}
{1: 12, 2: 31, 3: 5}
[(2, 31), (1, 12), (3, 5)]
I was under the impression that the [(2, 31), (1, 12), (3, 5)] would be converted to a dict
Any help to understand where or what I'm doing wrong would help! Thanks!
a=[2, 1, 3]
b=[31, 12, 5]
from collections import OrderedDict
print(OrderedDict(zip(a,b)))
You cannot sort a dictionary, in your case if you wanted to display sorted key/values of your dictionary you can convert it to a list of tuples as you have and sort it by whichever element you want. In the code below it creates a list of tuples and sorts by the first element in the tuples:
l1,l2=[2, 1, 3],[31, 12, 5]
print ([(one,two) for (one,two) in
sorted(zip(l1,l2),key=lambda pair: pair[0])])
prints:
[(1, 12), (2, 31), (3, 5)]
shoutout to Sorting list based on values from another list? for the help
Either that or create a list of the dictionaries keys and sort the list then loop through the list and call each key
Or use ordered dict as others have pointed out

For each in list Python

Consider I have a tuple in Python
numbers = (('One', 1), ('Two', 2), ('Three', 3), ('Four', 4))
I want to create a numbers_dict in dictionary
numbers_dict = {}
for number in numbers:
numbers_dict[number[0]] = number[1]
and so I get
numbers_dict = {
'One': 1,
'Two': 2,
'Three': 3,
'Four': 4,
}
Can I do something simpler using the syntax
numbers_dict = [n[0] : n[1] for n in numbers]
?
Just pass numbers directly to the dict() type:
numbers_dict = dict(numbers)
From the documentation:
If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, the positional argument must be an iterable object. Each item in the iterable must itself be an iterable with exactly two objects. The first object of each item becomes a key in the new dictionary, and the second object the corresponding value.
Your numbers tuple is such an iterable, and each element in it is a (key, value) pair.
Demo:
>>> numbers = (('One', 1), ('Two', 2), ('Three', 3), ('Four', 4))
>>> dict(numbers)
{'One': 1, 'Four': 4, 'Three': 3, 'Two': 2}
You could use a dictionary comprehension too, but that's really overkill here; only use that when you do something more meaningful for each key or value. But for completeness sake, that'd look like this:
numbers_dict = {k: v for k, v in numbers}

Python 3 sort a dict by its values [duplicate]

This question already has answers here:
How do I sort a dictionary by value?
(34 answers)
Closed last month.
The only methods I found work for python2 or return only list of tuples.
Is it possible to sort dictionary, e.g. {"aa": 3, "bb": 4, "cc": 2, "dd": 1}, by its values?
Order of sorted dictionary I want to achieve is from largest to smallest. I want results to look like this:
bb 4
aa 3
cc 2
dd 1
And after sorting I want to store it into a text file.
itemgetter (see other answers) is (as I know) more efficient for large dictionaries but for the common case, I believe that d.get wins. And it does not require an extra import.
>>> d = {"aa": 3, "bb": 4, "cc": 2, "dd": 1}
>>> for k in sorted(d, key=d.get, reverse=True):
... k, d[k]
...
('bb', 4)
('aa', 3)
('cc', 2)
('dd', 1)
Note that alternatively you can set d.__getitem__ as key function which may provide a small performance boost over d.get.
from collections import OrderedDict
from operator import itemgetter
d = {"aa": 3, "bb": 4, "cc": 2, "dd": 1}
print(OrderedDict(sorted(d.items(), key = itemgetter(1), reverse = True)))
prints
OrderedDict([('bb', 4), ('aa', 3), ('cc', 2), ('dd', 1)])
Though from your last sentence, it appears that a list of tuples would work just fine, e.g.
from operator import itemgetter
d = {"aa": 3, "bb": 4, "cc": 2, "dd": 1}
for key, value in sorted(d.items(), key = itemgetter(1), reverse = True):
print(key, value)
which prints
bb 4
aa 3
cc 2
dd 1
You can sort by values in reverse order (largest to smallest) using a dictionary comprehension:
{k: d[k] for k in sorted(d, key=d.get, reverse=True)}
# {'b': 4, 'a': 3, 'c': 2, 'd': 1}
If you want to sort by values in ascending order (smallest to largest)
{k: d[k] for k in sorted(d, key=d.get)}
# {'d': 1, 'c': 2, 'a': 3, 'b': 4}
If you want to sort by the keys in ascending order
{k: d[k] for k in sorted(d)}
# {'a': 3, 'b': 4, 'c': 2, 'd': 1}
This works on CPython 3.6+ and any implementation of Python 3.7+ because dictionaries keep insertion order.
Another way is to use a lambda expression. Depending on interpreter version and whether you wish to create a sorted dictionary or sorted key-value tuples (as the OP does), this may even be faster than the accepted answer.
d = {'aa': 3, 'bb': 4, 'cc': 2, 'dd': 1}
s = sorted(d.items(), key=lambda x: x[1], reverse=True)
for k, v in s:
print(k, v)
To sort dictionary, we could make use of operator module. Here is the operator module documentation.
import operator #Importing operator module
dc = {"aa": 3, "bb": 4, "cc": 2, "dd": 1} #Dictionary to be sorted
dc_sort = sorted(dc.items(),key = operator.itemgetter(1),reverse = True)
print dc_sort
Output sequence will be a sorted list :
[('bb', 4), ('aa', 3), ('cc', 2), ('dd', 1)]
If we want to sort with respect to keys, we can make use of
dc_sort = sorted(dc.items(),key = operator.itemgetter(0),reverse = True)
Output sequence will be :
[('dd', 1), ('cc', 2), ('bb', 4), ('aa', 3)]
To sort a dictionary and keep it functioning as a dictionary afterwards, you could use OrderedDict from the standard library.
If that's not what you need, then I encourage you to reconsider the sort functions that leave you with a list of tuples. What output did you want, if not an ordered list of key-value pairs (tuples)?

Python Compare List to Dict Value

Updated in an attempt to be more clear
I have three list of dictionaries that I want to merge into one based on a value.
The lists looks like this. They vary in how many dictionaries that they can have.
unplanned = [{'service__name': u'Email', 'service_sum': 4}, {'service__name': u'Peoplesoft', 'service_sum': 2}]
planned = [{'service__name': u'Email', 'service_sum': 2}, {'service__name': u'Gopher', 'service_sum': 2}, {'service__name': u'Peoplesoft', 'service_sum': 4}]
emerg = [{'service__name': u'Internet', 'service_sum': 1}]
I want to take the 3 lists and and create a new list that has the name's from all 3 lists and the values or 0 in a set order. So I am thinking something like this.
[(Email, (4, 2, 0)), (Peoplesoft, (2, 4, 0)), Gopher, (0, 2, 0)), Internet, (0, 0, 1))]
I thought I should create a list of the service__name's to compare against each list so I did that but I am not sure how to compare the 3 lists against this name list. I thought izip_longest would work but have no idea how to implement it. I am using 2.7.
Just use a dict, then convert it into a list afterwards:
some_list = [{'service__name': u'Email', 'service_sum': 4}, {'service__name': u'Email', 'service_sum': 1}, {'service__name': u'Network', 'service_sum': 0}]
def combine(list):
combined = {}
for item in list:
if item['service__name'] not in combined:
combined[item['service__name']] = []
combined[item['service__name']].append(item['service_sum'])
return combined.items()
combine(some_list) # [(u'Email', [4, 1]), (u'Network', [0])]
combine(unplanned)
combine(emerg + planned)
.....
Here's the version of the function that uses defaultdict:
def combine(list):
from collections import defaultdict
combined = defaultdict(list)
for item in list:
combined[item['service__name']].append(item['service_sum'])
return combined.items()
A little cleaner, but there's an unnecessary import, and a few other problems with it that may pop up in the future if the function definition is changed (see comments).
It seems like you could do something like:
output = []
for dicts in zip(unplanned,planned,emerg):
output.append(('Email',tuple(d['service_sum'] if d['service__name'] == 'Email' else 0 for d in dicts)))
Try the following codes. You can give variables better name since you know better about the contexts.
def convert(unplanned, planned, emerg):
chain = (unplanned, planned, emerg)
names = map(lambda lst: [d['service__name'] for d in lst], chain)
sums = map(lambda lst: [d['service_sum'] for d in lst], chain)
ds = [dict(zip(n, s)) for n,s in zip(names, sums)]
unique_names = set([])
unique_names = reduce(unique_names.union,names)
results = []
for n in unique_names:
s = []
for i in range(3):
s.append(ds[i].get(n,0))
results.append((n, tuple(s)))
return results
print convert(unplanned, planned, emerg)
The output at my machine is
[(u'Internet', (0, 0, 1)), (u'Peoplesoft', (2, 4, 0)), (u'Email', (4, 2, 0)), (u'Gopher', (0, 2, 0))]

Categories