Reputation: 133
I have a list of the folowing form:
oldlist = [{'x': {'a':1,'b':2}, 'y':2},{'x':{'a':6,'b':7}, 'y':2},{'x':{'a':1,'b':2}, 'y':3},{'x':{'a':1,'b':2}, 'y':2},{'x':{'a':10,'b':11}, 'y':4}]
to be converted into
final = [{'x':{'a':1,'b':2},'y':[2,3,2],'count':3},{'x':{'a':6,'b':7},'y':[2],'count':1},{'x':{'a':10,'b':11},'y':[4],'count':1}]
I have tried
oldlist = [{'x': {'a':1,'b':2}, 'y':2},{'x':{'a':6,'b':7}, 'y':2},{'x':{'a':1,'b':2}, 'y':3},{'x':{'a':1,'b':2}, 'y':2},{'x':{'a':10,'b':11}, 'y':4}]
list1=[]
list2=[]
list3=[]
s = set([d['x'] for d in oldlist])
news=list(s)
for item in oldlist:
if item['x'] == news[0]:
list1.append(item['y'])
if item['x'] == news[1]:
list2.append(item['y'])
if item['x'] == news[2]:
list3.append(item['y'])
final=[]
dic1 = {'x':news[0],'y':list1,'count':len(list1)}
dic2 = {'x':news[1],'y':list2,'count':len(list2)}
dic3 = {'x':news[2],'y':list3,'count':len(list3)}
final.append(dic1)
final.append(dic2)
final.append(dic3)
print final
Getting
s = set([d['x'] for d in oldlist])
TypeError: unhashable type: 'dict'
Is there a simpler way to do it? Plus here I knew that x can have only three values so I created three variables list1, list2 and list3. What if x can have several other values and I have to find out a similar list of dictionaries like final! It should also work for strings!
EDIT:I tried this. But it all got messed up
s = list(frozenset(oldlist[0]['x'].items()))
print s
for item in oldlist:
s.append(frozenset(item['x'].items()))
Upvotes: 0
Views: 1091
Reputation: 17263
You can use defaultdict
where keys are frozenset
objects created from value of x
in the original dicts and values are list of relative y
. Then you can construct the final result with list comprehension and turn frozensets
back to dicts:
from collections import defaultdict
oldlist = [{'x': {'a':1,'b':2}, 'y':2},{'x':{'a':6,'b':7}, 'y':2},{'x':{'a':1,'b':2}, 'y':3},{'x':{'a':1,'b':2}, 'y':2},{'x':{'a':10,'b':11}, 'y':4}]
res = defaultdict(list)
for d in oldlist:
res[frozenset(d['x'].items())].append(d['y'])
final = [{'x': dict(k), 'y': v, 'count': len(v)} for k, v in res.items()] # [{'y': [2, 3, 2], 'x': {'a': 1, 'b': 2}, 'count': 3}, {'y': [4], 'x': {'a': 10, 'b': 11}, 'count': 1}, {'y': [2], 'x': {'a': 6, 'b': 7}, 'count': 1}]
Upvotes: 1
Reputation: 7364
Set function of python does not allow dictionaries and you can not force it, try another method instead. (Take a closer look of the comment on the 5th and 6th line)
Try this code:
oldlist = [{'x': {'a':1,'b':2}, 'y':2},{'x':{'a':6,'b':7}, 'y':2},{'x':{'a':1,'b':2}, 'y':3},{'x':{'a':1,'b':2}, 'y':2},{'x':{'a':10,'b':11}, 'y':4}]
list1=[]
list2=[]
list3=[]
s = [d['x'] for d in oldlist] # Placed the dictionaries in a list
s = result = [dict(tupleized) for tupleized in set(tuple(item.items()) for item in s)] # This is the manual way on removing duplicates dictionaries in a list instead of using set
news=list(s)
for item in oldlist:
if item['x'] == news[0]:
list1.append(item['y'])
if item['x'] == news[1]:
list2.append(item['y'])
if item['x'] == news[2]:
list3.append(item['y'])
final=[]
dic1 = {'x':news[0],'y':list1,'count':len(list1)}
dic2 = {'x':news[1],'y':list2,'count':len(list2)}
dic3 = {'x':news[2],'y':list3,'count':len(list3)}
final.append(dic1)
final.append(dic2)
final.append(dic3)
print final
Upvotes: 1
Reputation: 11070
The set function can only handle hashable
objects, like a string, number, tuple e.t.c
Data types like List, dict are unhashable types, and hence the set function cannot handle them.
For some more clarity:
What do you mean by hashable in Python?
http://blog.lerner.co.il/is-it-hashable-fun-and-games-with-hashing-in-python/
A basic implementation of what you need:
for elem in oldlist:
found = False
for item in newlist:
if elem['x'] == item['x']:
y = item.get('y',[])
item['y'] = t.append(elem['y'])
found = True
break
if not found:
newlist.append({'x':elem['x'], 'y':[elem['y']]})
This will give you the expected result
Upvotes: 3