Reputation: 77
This is an example dictionary. The original dictionary has more keys. I didn't include them because it was unnecessary clutter. This dictionary is just a basic representation of my problem.
I've got a list of dictionaries, as follows:
colour_dict = [
{'main_colour': 'red', 'count': 1},
{'main_colour': 'blue', 'count': 5},
{'main_colour': 'green', 'count': 10},
]
and a list of colours, as follows:
colours = ["blue", "blue", "red", "greed", "red", "black"]
While I'm iterating over the list of colours, I'd like to check whether colour in the list colours already exist in the list of dictionaries colour_dict
. If it does exist increase the count by 1 and if it doesn't add it to the dictionary.
This is what I've got so far:
for colour in colours:
if any(d['main_colour'] == colour for d in colour_dict):
#Increase counter of that colour
else:
colour_dict.append({
'main_colour': colour, 'count': 1
})
Upvotes: 0
Views: 1727
Reputation: 23089
If you need your data to be in the format you show, and don't want to incur the cost of iterating over that list for every addition, that's not a problem. All you have to do is build an index for that data structure. The index will be a map with keys that are colors and values that are the index of that color in your original structure. You build this index first, and then you use it to process new entries efficiently. Here's how that goes:
colour_dict = [
{'main_colour': 'red', 'count': 1},
{'main_colour': 'blue', 'count': 5},
{'main_colour': 'green', 'count': 10},
]
colours = ["blue", "blue", "red", "greed", "red", "black"]
# Build an index mapping colors to positions in the 'colour_dict' list
index = {}
for i, entry in enumerate(colour_dict):
index[entry['main_colour']] = i
# Iterate over the new values, tallying them in the original structure, and
# adding new entries to both the original structure and the index when
# we discover colors that aren't yet in our structure. Note that there
# is just a single lookup per addition to the structure. No per-addition
# iteration here.
for colour in colours:
if colour in index:
colour_dict[index[colour]]['count'] += 1
else:
index[colour] = len(colour_dict)
colour_dict.append({'main_colour': colour, 'count': 1})
# Show the updated original structure
print(colour_dict)
Result:
[
{'main_colour': 'red', 'count': 3},
{'main_colour': 'blue', 'count': 7},
{'main_colour': 'green', 'count': 10},
{'main_colour': 'greed', 'count': 1},
{'main_colour': 'black', 'count': 1}
]
I am a teacher of programming, and I think this question is an opportunity to highlight an often overlooked technique. You don't have to change your existing data structure that isn't efficient to look things up in to be able to look things up in it efficiently. You can rather build an index into that structure that is efficient to look things up in that points back to the original structure for storage. This is a sort of "have your cake and eat it too" situation. It's worth grasping so you can have it in your bag of tricks.
This is like maintaining an index at the back of a book rather than restructuring the book's content to be easier to search for individual concepts at the cost of making it less valuable to read from front to back. A book index likewise gives you the best of both worlds.
Upvotes: 5
Reputation: 14141
The straightforward (but not very fast) code may be:
for colour in colours:
for dic in colour_dict:
if dic['main_colour'] == colour:
dic['count'] += 1
break
else:
colour_dict.append({'main_colour': colour, 'count': 1})
The result:
[{'main_colour': 'red', 'count': 3}, {'main_colour': 'blue', 'count': 7}, {'main_colour': 'green', 'count': 10}, {'main_colour': 'greed', 'count': 1}, {'main_colour': 'black', 'count': 1}]
The explanation:
The else
branch of the for
loop will execute only if the loop is fully exhausted, i.e. if no break statement finished it prematurely.
Upvotes: 1
Reputation: 778
This is the best that I can come up with. It only iterates over the list of dictionaries once per colour.
for colour in colours:
c_dict = list(filter(lambda c: c['main_colour']==colour, colour_dict))
# if it exists c_dict points at the actual value within the colour_dict list
# This means c_dict can be simply updated
if len(c_dict) != 0:
c_dict[0]['count'] += 1
else:
colour_dict.append({'main_colour':colour, 'count':1})
Upvotes: 1
Reputation: 10624
Your code looks fine, just missing the part of count increase. Here is the complete code:
for colour in colours:
if any(d['main_colour'] == colour for d in colour_dict):
for i in range(len(colour_dict)):
if colour in colour_dict[i].values():
colour_dict[i]['count']+=1
else:
colour_dict.append({
'main_colour': colour, 'count': 1
})
Result for given data:
>>> print(colour_dict)
[{'main_colour': 'red', 'count': 3}, {'main_colour': 'blue', 'count': 7}, {'main_colour': 'green', 'count': 10}, {'main_colour': 'greed', 'count': 1}, {'main_colour': 'black', 'count': 1}]
Upvotes: 1
Reputation: 162
If you structured your data in a different way it would be easier to use.
You can also take a look at the standard library's defaultdict
.
from collections import defaultdict
colour_dict = defaultdict(int) # produces int() == 0 on entry creation
colour_dict.update({
'red': 1,
'blue': 5,
'green': 10,
})
colours = ["blue", "blue", "red", "greed", "red", "black"]
for c in colours:
colour_dict[c] += 1
Upvotes: 0