简体   繁体   中英

Filter a list of dictionaries based on key (not key value) in Python

I would like to filter a list of (different) dictionaries based on a single key, not the value this key holds.

This is an example:

diclist_example = [{'animal': 'dog', 'legs': 'four'}, 
                   {'tvshow': 'Game of Thrones','rating': 'good'},
                   {'food': 'banana','color': 'yellow'},
                   {'animal': 'sheep', 'legs': 'four'}, 
                   {'tvshow': 'Gossip Girl','rating': 'bad'},
                   {'food': 'pizza','color': 'red-ish'}]

The problem with this is when I try to work with the 'animal' values for example, I get a KeyError because some dictonaries do not contain the 'animal' value.

I would like to generate a new list of dictionaries, containing all the dictionaries based on a key, for example, a list with all the animals, a list with all the tvshows of a list with all the foods.

You can do it with a lambda function that filter elements with a list comprehension. This function will return a list of all identical elements

diclist_example = [{'animal': 'dog', 'legs': 'four'}, 
                   {'tvshow': 'Game of Thrones', 'rating': 'good'},
                   {'food': 'banana','color': 'yellow'},
                   {'animal': 'sheep', 'legs': 'four'}, 
                   {'tvshow': 'Gossip Girl', 'rating': 'bad'},
                   {'food': 'pizza', 'color': 'red-ish'}]

getAllElementsWithSameKey = lambda key: [d for d in diclist_example if key in d]
getAllElementsWithSameKey('animal')

You can create a list of all your dictionaries with

allElements = [getAllElementsWithSameKey(k) for k in ('animal','tvshow','food')]

create a list of dictionaries automatically using defaultdict .

from collections import defaultdict

diclist_example=[{'animal': 'dog', 'legs': 'four'},
                {'tvshow': 'Game of Thrones','rating': 'good'},
                {'food': 'banana','color': 'yellow'},
                {'animal': 'sheep', 'legs': 'four'},
                {'tvshow': 'Gossip Girl','rating': 'bad'},
                {'food': 'pizza','color': 'red-ish'}]

diclist = defaultdict(list)
for l in diclist_example:
    for k in ('animal','food','tvshow'):
        if k in l:
            diclist[k].append(l)
            break

print(diclist)

result:

defaultdict(<class 'list'>, {'food': [{'color': 'yellow', 'food': 'banana'}, {'color': 'red-ish', 'food': 'pizza'}], 'tvshow': [{'rating': 'good', 'tvshow': 'Game of Thrones'}, {'rating': 'bad', 'tvshow': 'Gossip Girl'}], 'animal': [{'legs': 'four', 'animal': 'dog'}, {'legs': 'four', 'animal': 'sheep'}]})

You should always check if the value exists before doing anything with it, using an if statement.

if myValue!=None: # or similar, depending on situation
    # do something

To apply it to your example:

def zipDict(dicts):
    allKeys = set(k for d in dicts for k in d.keys())
    return {k:[d[k] for d in dicts if (k in d)] for k in allKeys}
                                   ###########  

Here is a more verbose version:

def zipDict(dicts):
    allKeys = set()
    for d in dicts:
        for key in d:
            allKeys.add(key)

    toReturn = {}
    for key in allKeys:
        toReturn[key] = []
        for d in dicts:
            if key in d: # THE GUARDIAN IF STATEMENT THAT PROTECTS YOU
                toReturn[key].append(d[key])

    return toReturn

sidenote: For other users who may stumble upon this post, here is a more efficient method: You should iterate over the dicts, NOT the allKeys, or else your algorithm is O(#keys * #dicts), rather than O(N):

def zipDict(dicts):
    R = {}
    for d in dicts:
        for k,v in d.items():
            if not k in R:  # A PROTECTIVE IF STATEMENT
                R[k] = []
            R[k].append(v)
    return R

You could use defaultdict to make it slightly nicer code:

def zipDict(dicts):
    R = defaultdict(list)
    for d in dicts:
        for k,v in d.items():
            R[k].append(v)
    return dict(R)

Ex [Hard coded]:

l = [dict_elt for dict_elt in diclist_example if 'animal' in dict_elt]

您也可以尝试以下方法:

filter(lambda x: x["animal"] if "animal" in x else None, diclist_example)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM