Python Groupby keys list of dictionaries - python-3.x

I have the following for instance:
x = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
and I would like to get a dictionary like this:
x = [{'A': [1,2], 'B': [1,2,3], 'C':[1], 'D': [1]}]
Do you have any idea how I could get this please?

You could use a collections.defaultdict of sets to collect unique values, then convert the final result to a dictionary with values as lists using a dict comprehension:
from collections import defaultdict
lst = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
result = defaultdict(set)
for dic in lst:
for key, value in dic.items():
result[key].add(value)
print({key: list(value) for key, value in result.items()})
Output:
{'A': [1, 2], 'B': [1, 2, 3], 'C': [1], 'D': [1]}
Although its probably better to add your data directly to the defaultdict to begin with, instead of creating a list of singleton dictionaries(don't recommend this data structure) then converting the result.

Using dict.setdefault
Ex:
x = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
res = {}
for i in x:
for k, v in i.items():
res.setdefault(k, set()).add(v)
#or res = [{k: list(v) for k, v in res.items()}]
print(res)
Output:
{'A': {1, 2}, 'B': {1, 2, 3}, 'C': {1}, 'D': {1}}

Related

Filter all dicts in a list to be with specific keys and ignore others?

I have the following list of dicts:
lst = [{'a':1, 'b':2, 'c':3}, {'a':1, 'b':2, 'd':3}, {'a':1, 'c':2, 'k':3}, {'d':1, 'k':2, 'l':3}]
I want to filter the list of dicts (in my case it's a list of thousands or even more dicts, with different keys with some overlap) to be a list containing all the dicts that have keys: ["a", "b"]. I want to filter each dict only to these a and b keys, and if they don't exist, don't include the dictionary in the final list. I am using:
[{"a": d.get("a"), "b": d.get("b")} for d in lst]
Please advise for an elegant way to solve it.
The dictionary keys-view is set-like, so it supports subset comparisons by using <= operator:
>>> keys = set("ab")
>>> [{k: d[k] for k in keys} for d in lst if keys <= d.keys()]
[{'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
I have figured it out and here is my alternative:
lst = [{'a':1, 'b':2, 'c':3}, {'a':1, 'b':2, 'd':3}, {'a':1, 'c':2, 'k':3}, {'d':1, 'k':2, 'l':3}]
keys = set("ab")
[i for i in [{k: d.get(k) for k in keys if k in d} for d in lst] if i]
Gives the desired answer:
[{'b': 2, 'a': 1}, {'b': 2, 'a': 1}, {'a': 1}]

Merge two dictionaries that have dictionaries as key values

Merge two dictionaries of dictionaries
My question is similar to this one, but the answers don't produce the right result (for me?).
Take these dictionaries:
a = {'a': {'a': 1}}
b = {'a': {'b': 2}}
I want to produce:
c = {'a': {'a': 1, 'b': 2}}
Using the answers from the quoted question, these all produce:
c = a.copy()
c.update(b)
>>
c == {'a': {'b': 2}
Consider that a and b might be more complex than this, for example:
a = {'a': {'aa': {'aaa': 1}, 'bb': {'bbb': 2}}}
b = {'a': {'bb': {'aaa': 1}, 'bb': {'bbb': 2}}}
In this case you can use
>>> a['a'].update(b['a'])
>>> a
{'a': {'a': 1, 'b': 2}}
Element in dictionary is also dictionary, so you can treat that element as dictionary.
As for more complex example I don't know what result should be. But in general, you can access elements in element as dictionary in nested for loops.

How to do sorting in Python3 for a list dictionary inside?

I have a list of the dictionary as follows:
[{"A":5,"B":10},
{"A":6,"B":13},
{"A":10,"B":5}]
I want to this list in decending order on the value of B. The output should look like this:
[{"A":6,"B":13},
{"A":5,"B":10},
{"A":10,"B":5}]
How to do that?
You can sort lists by the results of applying a function to each element: https://docs.python.org/3.9/library/functions.html#sorted
>>> data = [{"A":5,"B":10},
... {"A":6,"B":13},
... {"A":10,"B":5}]
>>> sorted(data, key=lambda dct: dct["B"], reverse=True)
[{'A': 6, 'B': 13}, {'A': 5, 'B': 10}, {'A': 10, 'B': 5}]

How to get list of indexes for unique values?

Having a list like this
lst = ['a','b','a','c','a','b','b']
I'd like to get lists of indexes for each unique element to get this:
indexes = {
'a': [0,2,4],
'b': [1,5,6],
'c': [3]
}
This is my current code, but I'm only getting the first index of each element.
indexes= dict()
for el in lst:
indexes[el] = [lst.index(el)]
>>> indexes
{'a': [0], 'b': [1], 'c': [3]}
Thanks for any help.
The problem with you code is you're overriding the same key again and again but with a different list, so your final dictionary contains only a single list.
you can avoid this behavior by using defaultdict.
from collections import defaultdict
lst = ["a", "b", "a", "c", "a", "b", "b"]
lst = [c for c in lst if c.strip()] # this will remove empty strings
indexes = defaultdict(list)
for index, char in enumerate(lst):
indexes[char].append(index)
indexes = dict(indexes)
print(indexes)
Output:
{'a': [0, 2, 4], 'b': [1, 5, 6], 'c': [3]}

A better way to create a dictionary out of two lists with duplicated values in one

I have two lists:
a = ["A", "B", "B", "C", "D", "A"]
b = [1, 2, 3, 4, 5, 6]
I want to have a dictionary like the one below:
d = {"A":[1, 6], "B":[2, 3], "C":[4], "D":[5]}
Right now I am doing something like this:
d = {i:[] for i in set(a)}
for c in zip(a, b):
d[c[0]].append(c[1])
Is there a better way to do this?
You can use the dict.setdefault method to initialize each key as a list and then append the current value to it while you iterate:
d = {}
for k, v in zip(a, b):
d.setdefault(k, []).append(v)
With the sample input, d would become:
{'A': [1, 6], 'B': [2, 3], 'C': [4], 'D': [5]}
Running your code, didnt produce the output what you wanted. The below is a bit more verbose but makes it easy to see what the code is doing.
a = ["A", "B", "B", "C", "D", "A"]
b = [1, 2, 3, 4, 5, 6]
c = {}
for a, b in zip(a, b):
if a in c:
if isinstance(a, list):
c[a].append(b)
else:
c[a] = [c[a], b]
else:
c[a] = b
print(c)
OUTPUT
{'A': [1, 6], 'B': [2, 3], 'C': 4, 'D': 5}
An alternative less verbose mode would be to use a default dict with list as the type, you can then append all the items to it. this will mean even single items will be in a list. to me its much cleaner as you will know the data type of each item in the list. However if you really do want to have single items not in a list you can clean it up with a dict comprehension
from collections import defaultdict
a = ["A", "B", "B", "C", "D", "A"]
b = [1, 2, 3, 4, 5, 6]
c = defaultdict(list)
for a, b in zip(a, b):
c[a].append(b)
d = {k: v if len(v) > 1 else v[0] for k, v in c.items()}
print(c)
print(d)
OUTPUT
defaultdict(<class 'list'>, {'A': [1, 6], 'B': [2, 3], 'C': [4], 'D': [5]})
{'A': [1, 6], 'B': [2, 3], 'C': 4, 'D': 5}

Resources