Updating dictionary with another k/v in python - python

I hate to ask this but I can't figure it out and it's getting to me.
I have to make a function that takes a given dictionary d1 and sort of compares it to another dictionary d2 then adds the compared value to d2.
d1 is already in the format needed to I don't have to worry about it.
d2 however, is a nested dictionary. It looks like this:
{’345’: {’Name’: ’xyzzy’, ’ID’: ’345’, ’Responses’: {’Q3’: ’c’, ’Q1’: ’a’, ’Q4’: ’b’, ’Q2’: ’a’}},
’123’: {’Name’: ’foo’, ’ID’: ’123’, ’Responses’: {’Q3’: ’c’, ’Q1’: ’a’, ’Q4’: ’a’, ’Q2’: ’b’}},
’234’: {’Name’: ’bar’, ’ID’: ’234’, ’Responses’: {’Q3’: ’c’, ’Q1’: ’a’, ’Q4’: ’b’, ’Q2’: ’b’}}}
So d1 is in the format of the Responses key, and that's what I need from d2 to compare it to d1.
So to do that I isolate responses:
for key, i in d2.items():
temp = i['Responses']
Now I need to run temp through a function with d1 that will output an integer. Then match that integer with the top-level key it came from and update a new k/v entry associated with it. But I don't know how to do this.
I've managed to update each top-level key with that compared value, but it only uses the first compared value for all the top-level keys. I can't figure out how to match the integer found to its key. This is what I have so far that works the best:
for i in d2:
score = grade_student(d1,temp) #integer
placement = {'Score': score}
d2[i].update(placement)

You could just iterate over sub dictionaries in d2 and update them once you've called grade_student:
for v in d2.values():
v['Score'] = grade_student(d1, v['Responses'])
Here's a complete example:
import pprint
d1 = {}
d2 = {
'345': {'Name': 'xyzzy', 'ID': '345', 'Responses': {'Q3': 'c', 'Q1': 'a', 'Q4': 'b', 'Q2': 'a'}},
'123': {'Name': 'foo', 'ID': '123', 'Responses': {'Q3': 'c', 'Q1': 'a', 'Q4': 'a', 'Q2': 'b'}},
'234': {'Name': 'bar', 'ID': '234', 'Responses': {'Q3': 'c', 'Q1': 'a', 'Q4': 'b', 'Q2': 'b'}}
}
# Dummy
def grade_student(x, y):
return 1
for v in d2.values():
v['Score'] = grade_student(d1, v['Responses'])
pprint.pprint(d2)
Output:
{'123': {'ID': '123',
'Name': 'foo',
'Responses': {'Q1': 'a', 'Q2': 'b', 'Q3': 'c', 'Q4': 'a'},
'Score': 1},
'234': {'ID': '234',
'Name': 'bar',
'Responses': {'Q1': 'a', 'Q2': 'b', 'Q3': 'c', 'Q4': 'b'},
'Score': 1},
'345': {'ID': '345',
'Name': 'xyzzy',
'Responses': {'Q1': 'a', 'Q2': 'a', 'Q3': 'c', 'Q4': 'b'},
'Score': 1}}

You don't have to iterate them. Use the built-in update() method. Here is an example
>>> A = {'cat':10, 'dog':5, 'rat':50}
>>> B = {'cat':5, 'dog':10, 'pig':20}
>>> A.update(B) #This will merge the dicts by keeping the values of B if collision
>>> A
{'rat': 50, 'pig': 20, 'dog': 10, 'cat': 5}
>>> B
{'pig': 20, 'dog': 10, 'cat': 5}

Related

Python one liner to merge dictionary which has common values

What I have:
a=[{'name':'a','vals':1,'required':'yes'},{'name':'b','vals':2},{'name':'d','vals':3}]
b=[{'name':'a','type':'car'},{'name':'b','type':'bike'},{'name':'c','type':'van'}]
What I tried:
[[i]+[j] for i in b for j in a if i['name']==j['name']]
What I got:
[[{'name': 'a', 'type': 'car'}, {'name': 'a', 'vals': 1}], [{'name': 'b', 'type': 'bike'}, {'name': 'b', 'vals': 2}]]
What I want:
[{'name': 'a', 'type': 'car','vals': 1},{'name': 'b', 'type': 'bike','vals': 2}]
Note:
I need to merge dicts into one dict.
It should merge only those have common 'name' in both a and b.
I want python one liner answer.
For Python 3, you can do this:
a=[{'name':'a','vals':1},{'name':'b','vals':2},{'name':'d','vals':3}]
b=[{'name':'a','type':'car'},{'name':'b','type':'bike'},{'name':'c','type':'van'}]
print([{**i,**j} for i in b for j in a if i['name']==j['name']])

In List of dicts, find dicts with most values [duplicate]

This question already has answers here:
Top-k on a list of dict in python
(3 answers)
Closed 2 years ago.
I have a list of python dicts like this:
[{'name': 'A', 'score': 12},
{'name': 'B', 'score': 20},
{'name': 'C', 'score': 11},
{'name': 'D', 'score': 20},
{'name': 'E', 'score': 9}]
How do I select first three dicts with highest score values? [D, B, A]
Sort using the score as a key, then take the top 3 elements:
>>> sorted([{'name': 'A', 'score': 12},
... {'name': 'B', 'score': 20},
... {'name': 'C', 'score': 11},
... {'name': 'D', 'score': 20},
... {'name': 'E', 'score': 9}], key=lambda d: d['score'])[-3:]
[{'name': 'A', 'score': 12}, {'name': 'B', 'score': 20}, {'name': 'D', 'score': 20}]

Iterate in a list of dictionaries and assign incremented values from another list

I have two lists which has values, I want to map the list2 value to list1 by referring to the 'cat' key. I want to iterate in list1, and if the 'cat' value of list1 is same as the previous 'cat' value, then it has to increment the list2 value and assign it into list1. If the value is different than the previous value, then the run breaks, and I want to go back to the first element of list2 and assign it.
Example:
list1 = [{'name':'aa','cat':'u1','Sno':1},
{'name':'bb','cat':'u1','Sno':2},
{'name':'bb','cat':'u1','Sno':3},
{'name':'ccc','cat':'u2','Sno':1},
{'name':'ccc','cat':'u2','Sno':2},
{'name':'cccc','cat':'u2','Sno':3},
{'name':'mmm','cat':'u3','Sno':1},
{'name':'nnn','cat':'u3','Sno':2},
{'name':'llll','cat':'u3','Sno':3}]
list2 = ['a','b','c','d','e','f','g','h']
Output should be like this:
output = [{'name':'aa','cat':'u1','Sno':1,'seed':'a'},
{'name':'bb','cat':'u1','Sno':2,'seed':'b'},
{'name':'bb','cat':'u1','Sno':3,'seed':'c'},
{'name':'ccc','cat':'u2','Sno':1,'seed':'a'},
{'name':'ccc','cat':'u2','Sno':2,'seed':'b'},
{'name':'cccc','cat':'u2','Sno':3,'seed':'c'},
{'name':'mmm','cat':'u3','Sno':1,'seed':'a'},
{'name':'nnn','cat':'u3','Sno':2,'seed':'b'},
{'name':'llll','cat':'u3','Sno':3,'seed':'c'}]
thanks for the suggestions
Okay, based on your clarification on your goal, this is actually pretty easy if you do an initialization and then simply iterate:
prev = ''
counter = 0
output = []
for e in list1:
if e['cat'] == prev: # This is the increment step
counter += 1
else: # This is the reset step
counter = 0
e['seed'] = list2[counter]
output.append(e)
prev = e['cat']
# output:
[{'Sno': 1, 'cat': 'u1', 'name': 'aa', 'seed': 'a'},
{'Sno': 2, 'cat': 'u1', 'name': 'bb', 'seed': 'b'},
{'Sno': 3, 'cat': 'u1', 'name': 'bb', 'seed': 'c'},
{'Sno': 1, 'cat': 'u2', 'name': 'ccc', 'seed': 'a'},
{'Sno': 2, 'cat': 'u2', 'name': 'ccc', 'seed': 'b'},
{'Sno': 3, 'cat': 'u2', 'name': 'cccc', 'seed': 'c'},
{'Sno': 1, 'cat': 'u3', 'name': 'mmm', 'seed': 'a'},
{'Sno': 2, 'cat': 'u3', 'name': 'nnn', 'seed': 'b'},
{'Sno': 3, 'cat': 'u3', 'name': 'llll', 'seed': 'c'}]
What matters really is the counter getting either incremented by 1 or reset to zero. Then you just assign the corresponding element to your collection. I am sure there must be an itertools method as well, but this should solve the issue with base Python.

Python: How to sort list of dictionaries by value and index

I have a list of a dictionary of data that is in order in some places and out of order in others:
Eg:
data = [{"text":'a', "value":1},
{"text":'b', "value":1},
{"text":'j', "value":2},
{"text":'k', "value":50},
{"text":'b', "value":50},
{"text":'y', "value":52},
{"text":'x', "value":2},
{"text":'k', "value":3},
{"text":'m', "value":3}]
I want to sort them as:
o = [{"text":'a', "value":1},
{"text":'b', "value":1},
{"text":'j', "value":2},
{"text":'x', "value":2},
{"text":'k', "value":3},
{"text":'m', "value":3},
{"text":'k', "value":50},
{"text":'b', "value":50},
{"text":'y', "value":52}]
wherein my sorting is some combination of the index of the item and the 2nd value, I was thinking sort with:
key=[(2nd value)<<len(closest power of 2 to len(index)) + index]
I can sort by the list of dicts by the 2nd value with:
data.sort(key= lambda x:x['value'])
How do I also add the index of the dictionary?
And is there a better sorting key I could use?
It appears that you're looking for the text field as a secondary sort key. The easiest way is to simply use a tuple for your keys, in priority order:
sorted(data, key=lambda x: (x['value'], x['text']) )
Does that yield what you need? Output:
[{'text': 'a', 'value': 1}, {'text': 'b', 'value': 1}, {'text': 'j', 'value': 2}, {'text': 'x', 'value': 2}, {'text': 'k', 'value': 3}, {'text': 'm', 'value': 3}, {'text': 'b', 'value': 50}, {'text': 'k', 'value': 50}, {'text': 'y', 'value': 52}]
The values (k, 50) and (b, 50) are now in the other order; I'm hopeful that I read your mind correctly.
UPDATE per OP clarification
I checked the docs. Python's sort method is stable, so you don't need the second sort key at all: in case of a tie, sort will maintain the original ordering:
>>> data.sort(key= lambda x:x['value'])
>>> data
[{'text': 'a', 'value': 1}, {'text': 'b', 'value': 1}, {'text': 'j', 'value': 2}, {'text': 'x', 'value': 2}, {'text': 'k', 'value': 3}, {'text': 'm', 'value': 3}, {'text': 'k', 'value': 50}, {'text': 'b', 'value': 50}, {'text': 'y', 'value': 52}]
... and this is what you requested.
Use enumerate to get the index and use that to sort
>>> res = [d for i,d in sorted(enumerate(data), key=lambda i_d: (i_d[1]['value'], i_d[0]))]
>>> pprint(res)
[{'text': 'a', 'value': 1},
{'text': 'b', 'value': 1},
{'text': 'j', 'value': 2},
{'text': 'x', 'value': 2},
{'text': 'k', 'value': 3},
{'text': 'm', 'value': 3},
{'text': 'k', 'value': 50},
{'text': 'b', 'value': 50},
{'text': 'y', 'value': 52}]
To sort it in-place, you can try using itertools.count
>>> from itertools import count
>>> cnt=count()
>>> data.sort(key=lambda d: (d['value'], next(cnt)))
>>> pprint(data)
[{'text': 'a', 'value': 1},
{'text': 'b', 'value': 1},
{'text': 'j', 'value': 2},
{'text': 'x', 'value': 2},
{'text': 'k', 'value': 3},
{'text': 'm', 'value': 3},
{'text': 'k', 'value': 50},
{'text': 'b', 'value': 50},
{'text': 'y', 'value': 52}]
>>>
Have you tried this:
sorted(data, key=lambda x: x['value'])

python efficient group by

I am looking for the most efficient way to extract items from a list of dictionaries.I have a list of about 5k dictionaries. I need to extract those records/items for which grouping by a particular field gives more than a threshold T number of records. For example, if T = 2 and dictionary key 'id':
list = [{'name': 'abc', 'id' : 1}, {'name': 'bc', 'id' : 1}, {'name': 'c', 'id' : 1}, {'name': 'bbc', 'id' : 2}]
The result should be:
list = [{'name': 'abc', 'id' : 1}, {'name': 'bc', 'id' : 1}, {'name': 'c', 'id' : 1}]
i.e. All the records with some id such that there are atleast 3 records of same id.
l = [{'name': 'abc', 'id' : 1}, {'name': 'bc', 'id' : 1}, {'name': 'c', 'id' : 1}, {'name': 'bbc', 'id' : 2}]
from collections import defaultdict
from itertools import chain
d = defaultdict(list)
T = 2
for dct in l:
d[dct["id"]].append(dct)
print(list(chain.from_iterable(v for v in d.values() if len(v) > T)))
[{'name': 'abc', 'id': 1}, {'name': 'bc', 'id': 1}, {'name': 'c', 'id': 1}]
If you want to keep them in groups don't chain just use each value:
[v for v in d.values() if len(v) > T] # itervalues for python2
[[{'name': 'abc', 'id': 1}, {'name': 'bc', 'id': 1}, {'name': 'c', 'id': 1}]]
Avoid using list as a variable as it shadows the python list type and if you had a variable list then the code above would cause you a few problems in relation to d = defaultdict(list)
to start out I would make a dictionary to group by your id
control = {}
for d in list:
control.setdefault(d['id'],[]).append(d)
from here all you have to do is check the length of control to see if its greater than your specified threshold
put it in a function like so
def find_by_id(obj, threshold):
control = {}
for d in obj:
control.setdefault(d['id'], []).append(d)
for val in control.values():
if len(val) > threshold:
print val

Categories