ylimes
ylimes

Reputation: 77

Check if value already exists within list of dictionaries and if it does update the counter

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

Answers (5)

CryptoFool
CryptoFool

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

MarianD
MarianD

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

J.Warren
J.Warren

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

IoaTzimas
IoaTzimas

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

spagh-eddie
spagh-eddie

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

Related Questions