Reputation: 21
I want to merge 2 arrays in Python and add values based on a key
Example:
Groceries = []
A = [{group: 'Fruit',
item: 'Banana',
quantity: 10},
{group: 'Vegetable',
item: 'Carrot',
quantity: 1}]
for item in A:
Groceries.append(item)
B = [{group: 'Fruit',
item: 'Banana',
quantity: 3},
{group: 'Vegetable',
item: 'Celery',
quantity: 1}]
for item in B:
Groceries.append(item)
print(Groceries) will return:
[{group: 'Fruit',
item: 'Banana',
quantity: 10},
{group: 'Vegetable',
item: 'Carrot',
quantity: 1},
{group: 'Fruit',
item: 'Banana',
quantity: 3},
{group: 'Vegetable',
item: 'Celery',
quantity: 1}]
Is there any way I can merge them in a way where the result would instead be:
[{group: 'Fruit',
item: 'Banana',
quantity: 13},
{group: 'Vegetable',
item: 'Carrot',
quantity: 1},
{group: 'Vegetable',
item: 'Celery',
quantity: 1}]
(Note that it states 13 Bananas instead of 2 instances of Bananas)
Upvotes: 2
Views: 1223
Reputation: 24133
Am inefficient way would be to search the list for an existing dictionary, matching the 'group'
and 'item'
:
for item in B:
existing = next((existing for existing in Groceries
if existing['group'] == item['group'] and
existing['item'] == item['item']),
None)
if existing:
existing['quantity'] += item['quantity']
else:
Groceries.append(item)
This is inefficient because it uses next
to do a linear search through all entries in Groceries. If you had millions of items it would become very slow.
If you restructure your data to be dictionary lookups based upon groups and items it will be much quicker. You need a dictionary of dictionaries. You can also use a defaultdict
to automatically have value 0 for missing items:
from collections import defaultdict
defaultitems = lambda: defaultdict(int)
quantities = defaultdict(defaultitems)
This allows you to create groups and items with quantity 0
:
>>> quantities['Fruit']['Tomato']
0
First create using A:
defaultitems = lambda: defaultdict(int)
quantities = defaultdict(defaultitems)
for item in A:
quantities[item['group']][item['item']] = item['quantity']
Then add B:
for item in B:
quantities[item['group']][item['item']] += item['quantity']
However, because you get the default you could add A and B all in one loop, by chaining them together (using chain
):
from itertool import chain
for item in chain(A, B):
quantities[item['group']][item['item']] += item['quantity']
Upvotes: 0
Reputation: 537
Groceries = []
A = [{'group': 'Fruit',
'item': 'Banana',
'quantity': 10},
{'group': 'Vegetable',
'item': 'Carrot',
'quantity': 1}]
for item in A:
Groceries.append(item)
B = [{'group': 'Fruit',
'item': 'Banana',
'quantity': 3},
{'group': 'Vegetable',
'item': 'Celery',
'quantity': 1}]
for item in B:
Groceries.append(item)
new_data = []
for i in Groceries:
yes = False
for s in new_data:
if s['item'] == i['item']:
s['quantity'] += i['quantity']
yes = True
if not yes:
new_data.append(i)
print new_data
You can loop through the list and add the quantity if value exists. This may help
Upvotes: 0
Reputation: 52949
For counting jobs, use a Counter
:
from collections import Counter
c = Counter()
The counter needs proper keys to differentiate items, so form (group, item)
tuples from your original dict format as keys, quantity as value:
c.update({(d["group"], d["item"]): d["quantity"] for d in A})
c.update({(d["group"], d["item"]): d["quantity"] for d in B})
This method has a caveat though: if your list A or B contains multiple records for the same item, this will not work correctly as the dictionary comprehension will "remember" only the latest. If you know this is a possibility, you can revert to plain old for-looping and adding up:
from operator import itemgetter
from itertools import chain
keymaster = itemgetter("group", "item")
for d in chain(A, B):
c[keymaster(d)] += d["quantity"]
To get your original format back create a list of small dictionaries from counter's items:
[{"group": k[0], "item": k[1], "quantity": v} for k, v in c.items()]
# results
[{'item': 'Carrot', 'group': 'Vegetable', 'quantity': 1},
{'item': 'Celery', 'group': 'Vegetable', 'quantity': 1},
{'item': 'Banana', 'group': 'Fruit', 'quantity': 13}]
Upvotes: 2
Reputation: 10101
Use a support method to do the merging and use a dictionary to store the results. This will do what you want but it will loose the ordering of the list. I am not sure if you need that.
Groceries = {}
A = [{'group': 'Fruit',
'item': 'Banana',
'quantity': 10},
{'group': 'Vegetable',
'item': 'Carrot',
'quantity': 1}]
for item in A:
Groceries[item['item']] = item
B = [{'group': 'Fruit',
'item': 'Banana',
'quantity': 3},
{'group': 'Vegetable',
'item': 'Celery',
'quantity': 1}]
def add_item(d, other):
key = other['item']
if d[key] in d:
d[key]['quantity'] += y['quantity']
else:
d[key] = y
for item in B:
add_item(Groceries, item)
print(Groceries.values())
[{'group': 'Fruit', 'quantity': 13, 'item': 'Banana'}, {'group': 'Vegetable', 'quantity': 1, 'item': 'Celery'}, {'group': 'Vegetable', 'quantity': 1, 'item': 'Carrot'}]
Upvotes: 0