sovoshib
sovoshib

Reputation: 41

Updating list of dictionaries with another list of dictionaries. Is there a faster way?

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

Answers (5)

Michael Anderson
Michael Anderson

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

lakshayg
lakshayg

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

dawg
dawg

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

U13-Forward
U13-Forward

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

DSM
DSM

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

Related Questions