filtrer les éléments dans un dictionnaire python où les clés contiennent une chaîne spécifique

95

Je suis un codeur C développant quelque chose en python. Je sais comment faire ce qui suit en C (et donc en logique C-like appliquée à python), mais je me demande quelle est la façon `` Python '' de le faire.

J'ai un dictionnaire d, et j'aimerais opérer sur un sous-ensemble d'éléments, seuls ceux dont la clé (chaîne) contient une sous-chaîne spécifique.

c'est-à-dire que la logique C serait:

for key in d:
    if filter_string in key:
        # do something
    else
        # do nothing, continue

J'imagine que la version python serait quelque chose comme

filtered_dict = crazy_python_syntax(d, substring)
for key,value in filtered_dict.iteritems():
    # do something

J'ai trouvé de nombreux articles ici concernant le filtrage des dictionnaires, mais je n'ai pas pu en trouver un qui impliquait exactement cela.

Mon dictionnaire n'est pas imbriqué et j'utilise python 2.7

note
la source

Réponses:

183

Que diriez-vous d'une compréhension de dict :

filtered_dict = {k:v for k,v in d.iteritems() if filter_string in k}

Une fois que vous le voyez, cela devrait être explicite, car il se lit assez bien comme l'anglais.

Cette syntaxe nécessite Python 2.7 ou supérieur.

Dans Python 3, il n'y a que dict.items(), pas iteritems()donc vous utiliseriez:

filtered_dict = {k:v for (k,v) in d.items() if filter_string in k}
Jonathon Reinhart
la source
1
Pourquoi pas filtered_dict = {k:d[k] for k in d if filter_string in k}?
thefourtheye
5
@thefourtheye Je vais deviner que le mien est plus rapide, car il n'entraîne pas la d[k]recherche.
Jonathon Reinhart
Aussi, dit-il # do somethingdans les commentaires, mais nous déposons quelques clés ici.
thefourtheye
Avons-nous iteritemsen Python 3? Je ne pense pas. Alors, ma version serait compatible, non?
thefourtheye
1
Dans Python 3, vous remplaceriez iteritemspar items, qui est identique à Python 2.7 iteritems.
Jonathon Reinhart
17

Optez pour ce qui est le plus lisible et le plus facile à maintenir. Ce n'est pas parce que vous pouvez l'écrire sur une seule ligne que vous devriez le faire. Votre solution existante est proche de ce que j'utiliserais autrement que j'utiliserais des itérités pour ignorer la recherche de valeur, et je déteste les if imbriqués si je peux les éviter:

for key, val in d.iteritems():
    if filter_string not in key:
        continue
    # do something

Cependant, si vous voulez vraiment que quelque chose vous permette d'itérer à travers un dict filtré, je ne ferais pas le processus en deux étapes de construction du dict filtré, puis de l'itérer, mais plutôt d'utiliser un générateur, car ce qui est plus pythonique (et génial) que un générateur?

Tout d'abord, nous créons notre générateur, et une bonne conception nous oblige à le rendre suffisamment abstrait pour être réutilisable:

# The implementation of my generator may look vaguely familiar, no?
def filter_dict(d, filter_string):
    for key, val in d.iteritems():
        if filter_string not in key:
            continue
        yield key, val

Et puis nous pouvons utiliser le générateur pour résoudre votre problème de manière agréable et propre avec un code simple et compréhensible:

for key, val in filter_dict(d, some_string):
    # do something

En bref: les générateurs sont géniaux.

Brendan F
la source
11

Vous pouvez utiliser la fonction de filtre intégrée pour filtrer les dictionnaires, listes, etc. en fonction de conditions spécifiques.

filtered_dict = dict(filter(lambda item: filter_str in item[0], d.items()))

L'avantage est que vous pouvez l'utiliser pour différentes structures de données.

Pulkit
la source
Notez que cela items:devrait être item:dans la définition lambda.
bkribbs
Merci @bkribbs d'avoir signalé l'erreur. Je l'ai rectifié maintenant.
Pulkit
8
input = {"A":"a", "B":"b", "C":"c"}
output = {k:v for (k,v) in input.items() if key_satifies_condition(k)}
jspurim
la source
3
Ma méthode en utilisant iteritems()va être plus efficace que items().
Jonathon Reinhart
@Jonathin Reinhart Je n'en savais rien. Merci.
jspurim
2
Sur Python 2.7 uniquement. En Python 3, il n'y en a que items() , qui agit comme Python 2.7 iteritems.
Jonathon Reinhart
1
La question est explicitement pour python 2.7
Brendan F
7

Jonathon vous a donné une approche utilisant des compréhensions dict dans sa réponse . Voici une approche qui traite de votre partie faire quelque chose .

Si vous voulez faire quelque chose avec les valeurs du dictionnaire, vous n'avez pas du tout besoin d'une compréhension du dictionnaire:

J'utilise iteritems() puisque vous avez tagué votre question avec

results = map(some_function, [(k,v) for k,v in a_dict.iteritems() if 'foo' in k])

Maintenant, le résultat sera dans une liste avec some_functionappliqué à chaque paire clé / valeur du dictionnaire, qui a foodans sa clé.

Si vous voulez simplement traiter les valeurs et ignorer les clés, changez simplement la compréhension de la liste:

results = map(some_function, [v for k,v in a_dict.iteritems() if 'foo' in k])

some_function peut être n'importe quel appelable, donc un lambda fonctionnerait également:

results = map(lambda x: x*2, [v for k,v in a_dict.iteritems() if 'foo' in k])

La liste interne n'est en fait pas requise, car vous pouvez également transmettre une expression de générateur à la carte:

>>> map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))
[4]
Burhan Khalid
la source
intéressant. comment la fonction some_function serait-elle définie? dans le premier cas (k, v), prend-il juste deux paramètres? première clé puis valeur?
mémo
Oui, juste un appelable. Alors map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))- cela vous donnera [4].
Burhan Khalid
C'est correct, mais plus pythonique que l'utilisation mapest une compréhension de liste. [f(v) for k, v in d.iteritems() if substring in k]Je pense que c'est beaucoup plus lisible et plus efficace.
Davidmh
@memo Cela ne prendrait pas deux paramètres, cela prendrait un seul paramètre avec deux éléments. Il y a aussi starmap qui va décompresser en deux arguments, cependant c'est un itérateur paresseux (doit être itéré avant qu'il ne s'exécute, c'est results = list(starmap(...))-à- dire ou for result in starmap(...): ...).
nmclean