Reputation: 41
I have a list of dictionaries that I need to update with information from another list of dictionaries. My current solution (below) works by taking every dictionary from the first list and comparing it to every dictionary in the second list. It works, but is there a faster, more elegant way of achieving the same result?
a = [ { "id": 1, "score":200 }, { "id": 2, "score":300 }, { "id":3, "score":400 } ]
b = [ { "id": 1, "newscore":500 }, { "id": 2, "newscore":600 } ]
# update a with data from b
for item in a:
for replacement in b:
if item["id"]==replacement["id"]:
item.update({"score": replacement["newscore"]})
Upvotes: 4
Views: 142
Reputation: 73480
Create a dictionary indexed by id
using the first array.
Loop through the second array using the id
.
for replacement in b:
v = lookup.get(replacement['id'], None)
if v is not None:
v['score'] = replacement['newscore']
This converts an O(n^2)
problem to an O(n)
problem.
Upvotes: 3
Reputation: 2173
If you are open to using pandas
and a, b are pandas dataframes then here is a oneliner
a.loc[a.id.isin(b.id), 'score'] = b.loc[b.id.isin(a.id), 'newscore']
Converting a, b to dataframes is simple, just use pd.DataFrame.from_records
Another way of doing this if you can change "newscore" to "score"
a = pd.DataFrame.from_records(a, index="id")
b = pd.DataFrame.from_records(b, index="id")
a.update(b)
Here are the timeit results
In [10]: %timeit c = a.copy(); c.update(b)
702 µs ± 37.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Upvotes: 0
Reputation: 103754
First create a dict of scores to update:
>>> new_d={d['id']:d for d in b}
>>> new_d
{1: {'id': 1, 'newscore': 500}, 2: {'id': 2, 'newscore': 600}}
Then iterate over a and update by id:
for d in a:
if d['id'] in new_d:
d['score']=new_d[d['id']]['newscore']
>>> a
[{'id': 1, 'score': 500}, {'id': 2, 'score': 600}, {'id': 3, 'score': 400}]
Which could be even simpler as:
new_d={d['id']:d['newscore'] for d in b}
for d in a:
if d['id'] in new_d:
d['score']=new_d[d['id']]
Upvotes: 0
Reputation: 71570
List Comprehension:
[i.update({"score": x["newscore"]}) for x in b for i in a if i['id']==x['id']]
print(a)
Output:
[{'id': 1, 'score': 500}, {'id': 2, 'score': 600}, {'id': 3, 'score': 400}]
Timing:
%timeit [i.update({"score": x["newscore"]}) for x in b for i in a if i['id']==x['id']]
Output:
100000 loops, best of 3: 3.9 µs per loop
Upvotes: 1
Reputation: 353009
Instead of doing a len(a) * len(b) loop, process b into something easier to work with:
In [48]: replace = {d["id"]: {"score": d["newscore"]} for d in b}
In [49]: new_a = [{**d, **replace.get(d['id'], {})} for d in a]
In [50]: new_a
Out[50]: [{'id': 1, 'score': 500}, {'id': 2, 'score': 600}, {'id': 3, 'score': 400}]
Note that the {**somedict}
syntax requires a modern version of Python (>= 3.5.)
Upvotes: 1