merge list of dictionaries which have list init - python

My current list:
my_list = [
{'id': 1, 'val': [6]},
{'id': 2, 'val': [7]},
{'id': 3, 'val': [8]},
{'id': 2, 'val': [9]},
{'id': 1, 'val': [10]},
]
Desired output:
my_list = [
{'id': 1, 'val': [6, 10]},
{'id': 2, 'val': [7, 9]},
{'id': 3, 'val': [8]},
]
what I tried so far:
my_new_list = []
id_set = set()
for d in my_list:
if d['id'] not in id_set:
id_set.add(d['id'])
temp = {'id': d['id'], 'val': d['val']}
my_new_list.append(temp)
else:
# loop over the new list and find the dict which already have d['id'] and update by appending value
# but this is not efficient
any other more efficient approach or may be some inbuilt function I'm not aware of.
PS: Order is important!

.setdefault() is your friend:
(We should use collections.OrderedDict to remember the order that keys were first inserted.)
>>> import collections
>>> result = collections.OrderedDict()
>>> for d in my_list:
... result.setdefault(d["id"], []).extend(d["val"])
>>> lst = []
>>> for k, v in result.items():
... lst.append({"id": k, "val": v})

Same approach as ozgur, but using collections.defaultdict:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for dd in my_list:
d[dd['id']].extend(dd['val'])
>>> d
defaultdict(<type 'list'>, {1: [6, 10], 2: [7, 9], 3: [8]})
>>>
>>> lst = []
>>> for k,v in d.iteritems():
lst.append({'id':k, 'val':v})
>>> lst
[{'id': 1, 'val': [6, 10]}, {'id': 2, 'val': [7, 9]}, {'id': 3, 'val': [8]}]
>>>

You can use itertools.groupby in order to sort and group the original list by 'id' and accumulate the 'val' for each group:
from itertools import groupby
key_fnc = lambda d: d['id']
result = [
{'id': k, 'val': sum([d['val'] for d in g], [])}
for k, g in groupby(sorted(my_list, key=key_fnc), key=key_fnc)
]

Related

Creating a dictionary of only the max common pairing of groups

I would like to create a dictionary of the max common pairings - an "agreement" table. Is it possible to shorten the code a bit when finding the agreement? As of now, I am not really liking finding the max count and then matching on the count to find the "agreement".
import pandas as pd
from collections import defaultdict
df = pd.DataFrame({
'id': ['A', 'A', 'B', 'B', 'B', 'B'],
'value': [1, 1, 2, 2, 1, 2]})
df = df.groupby(["id","value"]).size().reset_index().rename(columns={0: "count"})
df["max_rank"] = df.groupby(["id"])["count"].transform("max")==df["count"]
df = df.loc[(df["max_rank"]==True)]
d = defaultdict(list)
for idx, row in df.iterrows():
d[row['id']].append(row['value'])
d = [{k: v} for k, v in d.items()]
d
output:
[{'A': [1]}, {'B': [2]}]
You can build a dict that maps each id to a list of values, and then use the collections.Counter.most_common method to obtain the most common value for each id:
from collections import Counter
d = {'id': ['A', 'A', 'B', 'B', 'B', 'B'], 'value': [1, 1, 2, 2, 1, 2]}
mapping = {}
for k, v in zip(d['id'], d['value']):
mapping.setdefault(k, []).append(v)
print({k: Counter(l).most_common(1)[0][0] for k, l in mapping.items()})
This outputs:
{'A': 1, 'B': 2}

Python - Find duplicates in list and group them by key

I have a list of python dicts like this:
[{
'id': 1,
'name': 'name1'
}, {
'id': 2,
'name': 'name2'
}, {
'id': 3,
'name': 'name1'
}]
What I want to do is to create a new list of dictionaries, containing only the ones that have the key 'name' duplicated, and group them.
[{
'id1': 1,
'id2': 3,
'name': 'name1'
}]
The first list is an SQL query output and I need to delete the rows that have the key 'name' duplicated, keeping only one.
You can use itertools.groupby:
import itertools
d = [{'id': 1, 'name': 'name1'}, {'id': 2, 'name': 'name2'}, {'id': 3, 'name': 'name1'}]
new_data = [[a, list(b)] for a, b in itertools.groupby(sorted(d, key=lambda x:x['name']), key=lambda x:x['name'])]
final_dicts = [{'name':a, **{f'id{i}':a['id'] for i, a in enumerate(b, 1)}} for a, b in new_data if len(b) > 1]
Output:
[{'name': 'name1', 'id1': 1, 'id2': 3}]
I suggest you the following solution, quite easy to read and understand:
from collections import defaultdict
ds = [{'id': 1, 'name': 'name1'},
{'id': 2, 'name': 'name2'},
{'id': 3, 'name': 'name1'}]
newd = defaultdict(list)
for d in ds:
newd[d['name']].append(d['id'])
# Here newd is {'name1': [1, 3], 'name2': [2]}
result = []
for k,v in newd.items():
if len(v) > 1:
d = {f'id{i}':i for i in v}
d['name'] = k
result.append(d)
print(result) # [{'id1': 1, 'id3': 3, 'name': 'name1'}]
You can use collections.Counter:
from collections import Counter
from operator import itemgetter
l = [{'id': 1, 'name': 'name1'}, {'id': 2, 'name': 'name2'}, {'id': 3, 'name': 'name1'}]
print([{'name': n, **{'id%d' % i: d['id'] for i, d in enumerate([d for d in l if d['name'] == n], 1)}} for n, c in Counter(map(itemgetter('name'), l)).items() if c > 1])
This outputs:
[{'name': 'name1', 'id1': 1, 'id2': 3}]

How to Create Nested Dictionary in Python with 3 lists

Let us Suppose, I have created 3 lists and I want to create a dictionary for it. e.g.
a= ['A', 'B', 'C', 'D']
b =[1, 2, 3, 4]
c = [9, 8, 7, 6]
Now What I want is to create a dictionary like this:
{'A':{'1' :'9'} , 'B':{'2':'8'}, 'C':{'3':'7'} , 'D':{'4':'6'}}
is it possible, Can Someone Help me on this?
You can create the dictionary from zip-ed lists and convert the int values to strings - if I understood your question proper
dct = {x: {str(y): str(z)} for x, y, z in zip(a,b,c)}
Output:
{'A': {'1': '9'}, 'C': {'3': '7'}, 'B': {'2': '8'}, 'D': {'4': '6'}}
You can also use map() here:
a = ['A', 'B', 'C', 'D']
b = [1, 2, 3, 4]
c = [9, 8, 7, 6]
dct = dict(map(lambda x, y, z : (x, {str(y): str(z)}), a, b, c))
print(dct)
Which outputs:
{'A': {'1': '9'}, 'B': {'2': '8'}, 'C': {'3': '7'}, 'D': {'4': '6'}}
{ a[x]: {b[x]: c[x]} for x in range(len(a))}
or if you really mean it:
{ a[x]: {str(b[x]): str(c[x])} for x in range(len(a))}
a = ['A', 'B', 'C', 'D'] # don't forget the quotation marks
b = [1, 2, 3, 4]
c = [9, 8, 7, 6]
res = dict()
for i, index_a in enumerate(a):
res[index_a] = {str(b[i]): c[i]}
Edit: Alternatively with list comprehension (mainly for the voters in here, as it's advanced python and harder to understand):
res = dict((a[i], {str(b[i]): c[i]}) for i in range(len(a)))
You can try this:
a= ['A', 'B', 'C', 'D']
b =[1, 2, 3, 4]
c = [9, 8, 7, 6]
new_data = dict([[a, dict([map(str, i)])] for a, i in zip(a, zip(b, c))])
Output:
{'A': {'1': '9'}, 'C': {'3': '7'}, 'B': {'2': '8'}, 'D': {'4': '6'}}
Or
new_data = dict(zip(a, map(lambda x:dict([x]), zip(map(str, b), map(str, c)))))
Assuming what you want is to have a be keys in the outer dictionary, and b and c the key and value element of the inner dicts:
d = {k: {x: y} for k, x, y in zip(a, b, c)}
Update:
However, in your example x and y are strings, so if that's what you want:
d = {k: {str(x): str(y)} for k, x, y in zip(a, b, c)}
Are you looking for something like this ?
a= ['A', 'B', 'C', 'D']
b =[1, 2, 3, 4]
c = [9, 8, 7, 6]
new_dict={}
set(map(lambda x,y,z:(new_dict.__setitem__(x,{y,z})),a,b,c))
print(new_dict)
output:
{'D': {4, 6}, 'A': {9, 1}, 'B': {8, 2}, 'C': {3, 7}}

Get data from dict by using for-loop in Python

My input is
[['apple',{'john':3,'anna':4,'kitty':6}],['pear',{'john':4,'anna':3,'kitty':3}]]
Expected output:
{
'key':['apple','pear'],
'value':[
{
'name':'john',
'data':[3,4]
},
{
'name':'anna',
'data':[4,3]
},
{
'name':'kitty',
'data':[6,3]
}
]
}
The key is a list which conclude the first part of each item, such as 'apple' 'pear', and the value is another list.
How should I do it?
You can achieve this with the help of collections.defaultdict:
from collections import defaultdict
value, key = defaultdict(list), []
for x in l:
key.append(x[0])
for k, v in x[1].items():
value[k].append(v)
To get the result:
In [15]: {'key': key, 'value': [{'name': k, 'data': v} for k, v in value.items()]}
Out[15]:
{'key': ['apple', 'pear'],
'value': [
{'data': [4, 3], 'name': 'anna'},
{'data': [6, 3], 'name': 'kitty'},
{'data': [3, 4], 'name': 'john'}]}
For a more efficient (?) version, subclass defaultdict to customize the default __missing__ hook to call the default_factory with missing key as a parameter (I copied this text and the implementation from the other answer of mine). Then you'll be able to do this in a single pass:
from collections import defaultdict
class mydefaultdict(defaultdict):
def __missing__(self, key):
self[key] = value = self.default_factory(key)
return value
# pass 'name' to the dictionary
value = mydefaultdict(lambda name: {'name': name, 'data': []})
key = []
for x in l:
key.append(x[0])
for k, v in x[1].items():
value[k]['data'].append(v)
The result is then
In [24]: {'key': key, 'value': value.values()}
Out[24]:
{'key': ['apple', 'pear'],
'value': [
{'data': [4, 3], 'name': 'anna'},
{'data': [6, 3], 'name': 'kitty'},
{'data': [3, 4], 'name': 'john'}]}
In Python 3, you'll have to call list(value.values()) instead of just value.values() to get a list object.
You can use following snippet:
input = [['apple',{'john':3,'anna':4,'kitty':6}],['pear',{'john':4,'anna':3,'kitty':3}]]
tmp = {}
output = {'key': [], 'value': []}
for item in input:
output['key'].append(item[0])
for name in item[1]:
try:
tmp[name].append(item[1][name])
except KeyError:
tmp[name] = [item[1][name]]
output['value'] = [{'name': name, 'data': data} for name, data in tmp.items()]
This function can help you
def map_data(data):
_tmp = {}
_keys = []
for _d in data:
_keys.append(_d[0])
for _k in _d[1].keys():
_v = _tmp.get(_k)
if not _v:
_v = {"name": _k, "data": []}
_v["data"].append(_d[1][_k])
_tmp[_k] = _v
return {"key": _keys, "value": [_v for _v in _tmp.values()]}
Here is my solution:
import json
my_list = [['apple',{'john':3,'anna':4,'kitty':6}],['pear',{'john':4,'anna':3,'kitty':3}]]
name_list = [item[1] for item in my_list] # [{'john': 3, 'kitty': 6, 'anna': 4}, {'john': 4, 'kitty
names = name_list[0].keys() # ['john', 'kitty', 'anna']
name_values = [[item[key] for item in name_list] for key in names] # [[3, 4], [6, 3], [4, 3]]
result = {
'key': [item[0] for item in my_list],
'value': [
{'name': name, 'value': value}
for (name, value) in zip(names, name_values)
]
}
print(json.dumps(result, indent=4))
And the output:
{
"value": [
{
"name": "john",
"value": [
3,
4
]
},
{
"name": "kitty",
"value": [
6,
3
]
},
{
"name": "anna",
"value": [
4,
3
]
}
],
"key": [
"apple",
"pear"
]
}
EDIT:
emmm, just found a better way to merge the dict value.
If the name_dict look like this one:
>>> name_dict
[{'john': [3], 'kitty': [6], 'anna': [4]}, {'john': [4], 'kitty': [3], 'anna': [3]}]
the task would be easy. What's the difference? The value is a list.
Now, we can use collections.Counter to merge two dicts!
>>> Counter(name_dict[0]) + Counter(name_dict[1])
Counter({'kitty': [6, 3], 'anna': [4, 3], 'john': [3, 4]})
so here is the new solution, we convert the value to a list first:(skip the 'key', only show the 'value'):
from collections import Counter
my_list = [['apple',{'john':3,'anna':4,'kitty':6}],['pear',{'john':4,'anna':3,'kitty':3}]]
name_list = [item[1] for item in my_list]
for item in name_list:
for key, value in item.items():
item[key] = [value]
name_values = dict(Counter(name_list[0]) + Counter(name_list[1])) # {'john': [3, 4], 'kitty': [6, 3], 'anna': [4, 3]}
print([{'name': name, 'value': value} for (name, value) in name_values.items()])
# output
[{'name': 'john', 'value': [3, 4]}, {'name': 'kitty', 'value': [6, 3]}, {'name': 'anna', 'value': [4, 3]}]

Python dictionary with same keys

I have a python list which contains dictionaries and I want to make a new list which contain dictionaries with unique keys and associated list values like below:
Input:
[{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
Output:
[{1:[2,3,3]},{2:[2,1]}]
Thanks in advance.
How about:
input = [{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
r = {}
for d in input:
# (assumes just one key/value per dict)
((x, y),) = d.items()
r.setdefault(x, []).append(y)
print [ {k: v} for (k, v) in r.items() ]
Result:
[{1: [2, 3, 3]}, {2: [2, 1]}]
[update]
just curious : Can you explain whats going on in ((x, y),) = d.items() and r.setdefault(x, []).append(y) ? – damned
First the ((x, y),) = d.items():
at this point, d will be an element from input, like {1: 2}
d.items() will be something analogous to [(1, 2)]
in order to unpack 1 and 2 into x and y, we need the extra , (otherwise the interpreter will think the outer parenthesis are doing grouping instead of defining a single element tuple)
The r.setdefault(x, []).append(y) is analogous to:
if not r.has_key(x):
r[x] = []
r[x].append(y)
Trick is to use dict.setdefault to start off a list and append to it:
input = [{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
output = {}
for d in input:
for k,v in d.items():
output.setdefault(k, []).append(v)
# output contains {1: [2, 3, 3], 2: [2, 1]}
output=[{k:v} for k,v in output.items()]
# output contains [{1: [2, 3, 3]}, {2: [2, 1]}]
What setdefault does is return either the existing list keyed by 'k', or if that key does not exist in the dictionary, it creates a new entry for that key with the second argument and returns that. Either way it returns the list whether it was pre-existing or new, so that you can then append to it.
>>> lis=[{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
>>> new_lis=[{}]
>>> for x in lis:
for y in x:
if y in new_lis[0]:
new_lis[0][y].append(x[y])
new_lis[0][y].sort()
else :new_lis[0][y]=[x[y]]
>>> new_lis
[{1: [2, 3, 3], 2: [1, 2]}]
>>> data = [{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
...
... from itertools import groupby
...
... keyFn = lambda x: x.keys()[0]
...
... [{k: [i.values()[0] for i in g]} for k, g in groupby(sorted(data, key=keyFn), keyFn)]
0: [{1: [2, 3, 3]}, {2: [2, 1]}]
output = []
for b in a:
added = 0
i = 0
for c in output:
if b.keys()[0] in c.keys():
output[i][b.keys()[0]].append(b[b.keys()[0]])
added = 1
i += 1
if not added:
output.append({b.keys()[0]: [b[b.keys()[0]]]})

Categories

Resources