Sort a list of one value dictionaries - python

I have data:
{'foo': [{1: 55}, {'c': 43}], 'bar': [{1: 43}, {'c': 40}]}
I wanna sort the dictionaries inside the foo and bar by its values:
{'foo': [{'c': 43}, {1: 55}], ...}
I dont wanna sort the 'foo' and 'bar'.
Is there an easy way yo do this? Sorry if I make mistake in writing the dicts/lists. Thanks

Here is one way. sorted accepts a key argument which accepts an anonymous lambda function. Since each dictionary only has one key/value, you can list your values and select the first.
d = {'foo': [{1: 55}, {'c': 43}], 'bar': [{1: 43}, {'c': 40}]}
res = {k: sorted(v, key=lambda x: list(x.values())[0]) for k, v in d.items()}
# {'bar': [{'c': 40}, {1: 43}], 'foo': [{'c': 43}, {1: 55}]}

Then tell it to sort by the value:
>>> x={'foo': [{1: 55}, {'c': 43}], 'bar': [{1: 43}, {'c': 40}]}
>>> for a in x: x[a].sort(key=lambda i: list(i.values())[0])
>>> x
{'bar': [{'c': 40}, {1: 43}], 'foo': [{'c': 43}, {1: 55}]}
Note since it's a dict, you have to retrieve all values, convert to list, and take first index. Probably not the best sturcture to sort.

As an alternative to kabanus' answer (which uses O(len(x)) dict lookup), this is an approach that use dict values view:
x={'a': [{1: 55}, {'c': 43}], 'b': [{1: 43}, {'c': 40}]}
for a in x.values(): a.sort(key=lambda i: list(i.values())[0])
print(x)
prints
{'a': [{'c': 43}, {1: 55}], 'b': [{'c': 40}, {1: 43}]}
Try it online!
I have not measured the performance of this method, but I expect it would be faster.

You can simply do:
data={'foo': [{1: 55}, {'c': 43}], 'bar': [{1: 43}, {'c': 40}]}
print({i:sorted(j,key=lambda x:list(x.values())) for i,j in data.items()})
output:
{'foo': [{'c': 43}, {1: 55}], 'bar': [{'c': 40}, {1: 43}]}

Related

Group all values with the same identic key in list of tuples

I have a list of tuples with strings and dictionaries which looks like following:
# data type: List<Tuple<string, dict>>
input_data_structure = [
('key1', {'a': 'b'}),
('key2', {'w': 'x'}),
('key1', {'c': 'd'}),
('key2', {'y': 'z'})]
I want to group alle values with the same keys. So the result could look like this or similiar:
# data type: List<Tuple<string, List<dict>>>
result_data_structure = [
('key1', [{'a': 'b'}, {'c': 'd'}]),
('key2', [{'w': 'x'}, {'y': 'z'}])]
For me it is important to have a good data structure, where I can loop through the existing arrays of the keys to get the values like this:
for t in result:
for val in t[1]:
print(val)
Does someone has an idea how to process or transform the data? Thanks in advance!
You can use defaultdicts to achieve easily this.
from collections import defaultdict
d = defaultdict(list)
for key, value in input_data_structure:
d[key].append(value)
d # defaultdict(<class 'list'>, {'key1': [{'a': 'b'}, {'c': 'd'}], 'key2': [{'w': 'x'}, {'y': 'z'}]})
If you need your output to be a list of tuples key/value, then you can just execute this line.
list(d.items()) # [('key1', [{'a': 'b'}, {'c': 'd'}]), ('key2', [{'w': 'x'}, {'y': 'z'}])]
A solution without import:
result = {}
for key, data in input_data_structure:
try:
result[key].append(data)
except KeyError:
result[key] = [data]
result = list(result.items())
This is a great place to use itertools.groupby, but remember that it only works properly on sorted data.
from itertools import groupby
from operator import itemgetter
input_data_structure = [
('key1', {'a': 'b'}),
('key2', {'w': 'x'}),
('key1', {'c': 'd'}),
('key2', {'y': 'z'})]
sorted_data = sorted(input_data_structure, key=itemgetter(0))
# [('key1', {'a': 'b'}), ('key1', {'c': 'd'}), ('key2', {'w': 'x'}), ('key2', {'y': 'z'})]
grouped_data = [(k, list(map(itemgetter(1), g))) for k, g in groupby(sorted_data, itemgetter(0))]
# [('key1', [{'a': 'b'}, {'c': 'd'}]), ('key2', [{'w': 'x'}, {'y': 'z'}])]

How to replace the keys of the dictionary with nested value dictionary of the specified format?

I have a dictionary of dictionaries as shown below:
dict_1 = {'a': {0: 'name_1', 1: 'name_2'}, 'b': {0: 23, 1: 53}}
and I would want it be in required format:
result = [{'a': 'name_1', 'b': 23}, {'a': 'name_2', 'b': 53}]
However, I get this in the following format with the code below:
l=[]
c={}
for k, v in d.items():
for i in v:
j=str(i)
j=j.replace(j, k)
c[j]=v[i]
print(c)
l.append(c)
The result is not of the required structure
[{'a': 'name_2', 'b': 53}, {'a': 'name_2', 'b': 53}]
Not sure how this would generalize, but you can try:
result = [dict(zip(dict_1, d2)) for d2 in
zip(*(d.values() for d in dict_1.values()))]
output: [{'a': 'name_1', 'b': 23}, {'a': 'name_2', 'b': 53}]
It looks like the innermost keys in dict_1 corresponds to the positions in the resulting list.
So, you can use a list comprehension to build up the new list of dictionaries. Set n to the size of the inner dict.
>>> pivots = list(dict_1) # The keys 'a' and 'b'
>>> n = 2
>>> [{k: dict_1[k][i] for k in pivots} for i in range(n)]
>>> [{'a': 'name_1', 'b': 23}, {'a': 'name_2', 'b': 53}]

Find matching pairs of dictionaries in a list using Python

In a given list:
unmatched_items_array = [{'c': 45}, {'c': 35}, {'d': 5}, {'a': 3.2}, {'a': 3}]
Find all 'key' pairs and print out and if no pairs found for given dictionary print out that dictionary.
What I managed to write so far sort of works but it keeps testing some items of the list even though they were already tested. Not sure how to fix it.
for i in range(len(unmatched_items_array)):
for j in range(i + 1, len(unmatched_items_array)):
# when keys are the same print matching dictionary pairs
if unmatched_items_array[i].keys() == unmatched_items_array[j].keys():
print(unmatched_items_array[i], unmatched_items_array[j])
break
# when no matching pairs print currently processed dictionary
print(unmatched_items_array[i])
Output:
{'c': 45} {'c': 35}
{'c': 45}
{'c': 35}
{'d': 5}
{'a': 3.2} {'a': 3}
{'a': 3.2}
{'a': 3}
What the output should be:
{'c': 45} {'c': 35}
{'d': 5}
{'a': 3.2} {'a': 3}
What am I doing wrong here?
Using collections.defaultdict
Ex:
from collections import defaultdict
unmatched_items_array = [{'c': 45}, {'c': 35}, {'d': 5}, {'a': 3.2}, {'a': 3}]
result = defaultdict(list)
for i in unmatched_items_array:
key, _ = i.items()[0]
result[key].append(i) #Group by key.
for _, v in result.items(): #print Result.
print(v)
Output:
[{'a': 3.2}, {'a': 3}]
[{'c': 45}, {'c': 35}]
[{'d': 5}]
With itertools.groupby:
from itertools import groupby
unmatched_items_array = [{'d': 5}, {'c': 35}, {'a': 3}, {'a': 3.2}, {'c': 45}]
for v, g in groupby(sorted(unmatched_items_array, key=lambda k: tuple(k.keys())), lambda k: tuple(k.keys())):
print([*g])
Prints:
[{'a': 3}, {'a': 3.2}]
[{'c': 35}, {'c': 45}]
[{'d': 5}]
EDIT: If your items in the list are sorted by keys already, then you can skip the sorted() call:
for v, g in groupby(unmatched_items_array, lambda k: tuple(k.keys()) ):
print([*g])

Sorting list of dicts by value of a key (or default-value, if key is missing)

Imagine that you have to sort a list of dicts, by the value of a particular key. Note that the key might be missing from some of the dicts, in which case you default to the value of that key to being 0.
sample input
input = [{'a': 1, 'b': 2}, {'a': 10, 'b': 3}, {'b': 5}]
sample output (sorted by value of key 'a')
[{'b': 5}, {'a': 1, 'b': 2}, {'a': 10, 'b': 3}]
note that {'b': 5} is first in the sort-order because it has the lowest value for 'a' (0)
I would've used input.sort(key=operator.itemgetter('a')), if all the dicts were guaranteed to have the key 'a'. Or I could convert the input dicts to collections.defaultdict and then sort.
Is there a way to do this in-place without having to creating new dicts or updating the existing dicts? Can operator.itemgetter handle missing keys?
>>> items = [{'a': 1, 'b': 2}, {'a': 10, 'b': 3}, {'b': 5}]
>>> sorted(items, key=lambda d: d.get('a', 0))
[{'b': 5}, {'a': 1, 'b': 2}, {'a': 10, 'b': 3}]
Or to update the existing dictionary in-place
items.sort(key=lambda d: d.get('a', 0))
Or if in sorted:
>>> items = [{'a': 1, 'b': 2}, {'a': 10, 'b': 3}, {'b': 5}]
>>> sorted(items,key=lambda x: x['a'] if 'a' in x else 0)
[{'b': 5}, {'a': 1, 'b': 2}, {'a': 10, 'b': 3}]
>>>

How to combine a list of dictionaries to one dictionary

I have a list of dicts:
d =[{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
I want to remove the curly braces and convert d to a single dict which looks like:
d ={'a': 4, 'b': 20, 'c': 5, 'd': 3}
If you don't mind duplicate keys replacing earlier keys you can use:
from functools import reduce # Python 3 compatibility
d = reduce(lambda a, b: dict(a, **b), d)
This merges the first two dictionaries then merges each following dictionary into the result built so far.
Demo:
>>> d =[{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
>>> reduce(lambda a, b: dict(a, **b), d)
{'a': 4, 'c': 5, 'b': 20, 'd': 3}
Or if you need this to work for arbitrary (non string) keys (and you are using Python 3.5 or greater):
>>> d =[{4: 4}, {20: 20}, {5: 5}, {3: 3}]
>>> reduce(lambda a, b: dict(a, **b), d) # This wont work
TypeError: keywords must be strings
>>> reduce(lambda a, b: {**a, **b}, d) # Use this instead
{4: 4, 20: 20, 5: 5, 3: 3}
The first solution hacks the behaviour of keyword arguments to the dict function. The second solution is using the more general ** operator introduced in Python 3.5.
You just need to iterate over d and append (update()) the element to a new dict e.g. newD.
d =[{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
newD = {}
for entry in d:
newD.update(entry)
>>> newD
{'c': 5, 'b': 20, 'a': 4, 'd': 3}
Note: If there are duplicate values in d the last one will be appear in newD.
Overwriting the values of existing keys, a brutal and inexperienced solution is
nd = {}
for el in d:
for k,v in el.items():
nd[k] = v
or, written as a dictionary comprehension:
d = {k:v for el in d for k,v in el.items()}
a = [{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
b = {}
[b.update(c) for c in a]
b = {'a': 4, 'b': 20, 'c': 5, 'd': 3}
if order is important:
from collections import OrderedDict
a = [{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
newD = OrderedDict()
[newD.update(c) for c in a]
out = dict(newD)

Categories

Resources