Dicts mélangés à des tableaux

21

Si vous avez vraiment besoin de modifier le dictionnaire d'origine:

empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
    del metadata[k]

Notez que nous devons faire une liste des clés vides car nous ne pouvons pas modifier un dictionnaire en l'itérant (comme vous l'avez peut-être remarqué). Cela coûte moins cher (en termes de mémoire) que de créer un tout nouveau dictionnaire, à moins qu'il n'y ait beaucoup d'entrées avec des valeurs vides.

nneonneo
la source
cela supprimera également la valeur 0 et 0 n'est pas vide
JVK
2
Si vous utilisez Python 3+ que vous devez remplacer .iteritems()par .items(), le premier ne fonctionne plus dans les dernières versions de Python.
Mariano Ruiz

Réponses:

12

La solution de BrenBarn est idéale (et pythonique, pourrais-je ajouter). Voici cependant une autre solution (fp):

from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
Joel Cornett
la source
12

Si vous souhaitez une approche complète, mais succincte, de la gestion des structures de données du monde réel qui sont souvent imbriquées et peuvent même contenir des cycles, je vous recommande de consulter l'utilitaire de remappage du package d'utilitaires boltons .

Après pip install boltonsou en copiant iterutils.py dans votre projet, faites simplement:

from boltons.iterutils import remap

drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)

Cette page contient de nombreux autres exemples, y compris ceux fonctionnant avec des objets beaucoup plus volumineux de l'API de Github.

C'est pur-Python, donc cela fonctionne partout, et est entièrement testé en Python 2.7 et 3.3+. Mieux encore, je l'ai écrit pour exactement des cas comme celui-ci, donc si vous trouvez un cas qu'il ne gère pas, vous pouvez me déranger pour le réparer ici .

Mahmoud Hashemi
la source
1
Cette solution a très bien fonctionné pour un problème similaire que j'avais: supprimer les valeurs vides des listes profondément imbriquées dans les dictionnaires. Merci!
Nicholas Tulach
1
C'est bien, car vous ne réinventez pas la roue et fournissez une solution pour les objets imbriqués. Merci!
vekerdyb
1
J'ai beaucoup aimé l'article que vous avez écrit pour votre bibliothèque, et c'est une bibliothèque utile!
lifelogger
11

Basé sur la solution de Ryan , si vous avez également des listes et des dictionnaires imbriqués:

Pour Python 2:

def remove_empty_from_dict(d):
    if type(d) is dict:
        return dict((k, remove_empty_from_dict(v)) for k, v in d.iteritems() if v and remove_empty_from_dict(v))
    elif type(d) is list:
        return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
    else:
        return d

Pour Python 3:

def remove_empty_from_dict(d):
    if type(d) is dict:
        return dict((k, remove_empty_from_dict(v)) for k, v in d.items() if v and remove_empty_from_dict(v))
    elif type(d) is list:
        return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
    else:
        return d
chachan
la source
1
Ha, belle extension! C'est une bonne solution pour les dictionnaires comme les suivants:d = { "things": [{ "name": "" }] }
Ryan Shea
6

Si vous avez un dictionnaire imbriqué et que vous voulez que cela fonctionne même pour des sous-éléments vides, vous pouvez utiliser une variante récursive de la suggestion de BrenBarn:

def scrub_dict(d):
    if type(d) is dict:
        return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
    else:
        return d
Ryan Shea
la source
Utiliser à la items()place de iteritems()pour Python 3
andydavies
6

Réponse rapide (TL; DR)

Exemple01

### example01 -------------------

mydict  =   { "alpha":0,
              "bravo":"0",
              "charlie":"three",
              "delta":[],
              "echo":False,
              "foxy":"False",
              "golf":"",
              "hotel":"   ",                        
            }
newdict =   dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(vdata) ])
print newdict

### result01 -------------------
result01 ='''
{'foxy': 'False', 'charlie': 'three', 'bravo': '0'}
'''

Réponse détaillée

Problème

  • Contexte: Python 2.x
  • Scénario: le développeur souhaite modifier un dictionnaire pour exclure les valeurs vides
    • aka supprimer les valeurs vides d'un dictionnaire
    • aka supprimer les clés avec des valeurs vides
    • aka dictionnaire de filtres pour les valeurs non vides sur chaque paire clé-valeur

Solution

  • exemple01 utiliser la syntaxe de compréhension de liste python avec un conditionnel simple pour supprimer les valeurs «vides»

Pièges

  • example01 ne fonctionne que sur une copie du dictionnaire original (ne modifie pas en place)
  • example01 peut produire des résultats inattendus selon ce que le développeur entend par "vide"
    • Le développeur veut-il conserver des valeurs fausses ?
    • Si les valeurs du dictionnaire ne sont pas garanties comme des chaînes, le développeur peut subir une perte de données inattendue.
    • result01 montre que seules trois paires clé-valeur ont été préservées de l'ensemble d'origine

Autre exemple

  • example02 aide à surmonter les pièges potentiels
  • L'approche consiste à utiliser une définition plus précise de «vide» en changeant le conditionnel.
  • Ici, nous ne voulons filtrer que les valeurs qui correspondent à des chaînes vides.
  • Ici, nous utilisons également .strip () pour filtrer les valeurs constituées uniquement d'espaces.

Exemple02

### example02 -------------------

mydict  =   { "alpha":0,
              "bravo":"0",
              "charlie":"three",
              "delta":[],
              "echo":False,
              "foxy":"False",
              "golf":"",
              "hotel":"   ",
            }
newdict =   dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(str(vdata).strip()) ])
print newdict

### result02 -------------------
result02 ='''
{'alpha': 0,
  'bravo': '0', 
  'charlie': 'three', 
  'delta': [],
  'echo': False,
  'foxy': 'False'
  }
'''

Voir également

dreftymac
la source
5

Pour python 3

dict((k, v) for k, v in metadata.items() if v)
Hector Oliveros
la source
4

En vous basant sur les réponses de patriciasz et nneonneo , et en tenant compte de la possibilité que vous souhaitiez supprimer des clés qui n'ont que certaines fausses choses (par exemple '') mais pas d'autres (par exemple 0), ou peut-être que vous voulez même inclure des choses véridiques (par exemple 'SPAM') , vous pouvez alors créer une liste de résultats très spécifique:

unwanted = ['', u'', None, False, [], 'SPAM']

Malheureusement, cela ne fonctionne pas tout à fait, car par exemple 0 in unwantedévalue à True. Nous devons faire la distinction entre les 0choses fausses et d'autres, nous devons donc utiliser is:

any([0 is i for i in unwanted])

... évalue à False.

Maintenant, utilisez-le pour delles choses indésirables:

unwanted_keys = [k for k, v in metadata.items() if any([v is i for i in unwanted])]
for k in unwanted_keys: del metadata[k]

Si vous voulez un nouveau dictionnaire, au lieu de le modifier metadatasur place:

newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
kwinkunks
la source
vraiment joli cliché, il aborde de nombreux problèmes à la fois et il résout la question, merci de le préciser
jlandercy
Cool! Cela fonctionne pour cet exemple. Cependant, cela ne fonctionne pas lorsqu'un élément du dictionnaire est[]
jsga
2

J'ai lu toutes les réponses dans ce fil et certaines se sont également référées à ce fil: Supprimez les dicts vides dans le dictionnaire imbriqué avec fonction récursive

J'ai initialement utilisé la solution ici et cela a très bien fonctionné:

Tentative 1: Trop chaud (pas performant ni à l'épreuve du temps) :

def scrub_dict(d):
    if type(d) is dict:
        return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
    else:
        return d

Mais certains problèmes de performances et de compatibilité ont été soulevés dans le monde Python 2.7:

  1. utiliser isinstanceau lieu detype
  2. dérouler la liste comp en forboucle pour plus d'efficacité
  3. utilisez python3 safe itemsau lieu deiteritems

Tentative 2: Trop froid (manque de mémorisation) :

def scrub_dict(d):
    new_dict = {}
    for k, v in d.items():
        if isinstance(v,dict):
            v = scrub_dict(v)
        if not v in (u'', None, {}):
            new_dict[k] = v
    return new_dict

DOH! Ce n'est pas récursif et pas du tout mémoizant.

Tentative 3: juste bien (jusqu'à présent) :

def scrub_dict(d):
    new_dict = {}
    for k, v in d.items():
        if isinstance(v,dict):
            v = scrub_dict(v)
        if not v in (u'', None, {}):
            new_dict[k] = v
    return new_dict
BlissRage
la source
1
sauf si je suis aveugle, il me semble que les
essais
1

Dicts mélangés à des tableaux

  • La réponse à tentative 3: Just Right (pour l'instant) de la réponse de BlissRage ne gère pas correctement les éléments des tableaux. J'inclus un patch au cas où quelqu'un en aurait besoin. La méthode gère la liste avec le bloc d'instructions de if isinstance(v, list):, qui nettoie la liste en utilisant l' scrub_dict(d)implémentation d' origine .
    @staticmethod
    def scrub_dict(d):
        new_dict = {}
        for k, v in d.items():
            if isinstance(v, dict):
                v = scrub_dict(v)
            if isinstance(v, list):
                v = scrub_list(v)
            if not v in (u'', None, {}):
                new_dict[k] = v
        return new_dict

    @staticmethod
    def scrub_list(d):
        scrubbed_list = []
        for i in d:
            if isinstance(i, dict):
                i = scrub_dict(i)
            scrubbed_list.append(i)
        return scrubbed_list
Marcello de Sales
la source
impressionnant . . .
J'avais
0

Une autre façon de procéder consiste à utiliser la compréhension du dictionnaire. Cela devrait être compatible avec2.7+

result = {
    key: value for key, value in
    {"foo": "bar", "lorem": None}.items()
    if value
}
Serguei Fedorov
la source
0

Voici une option si vous utilisez pandas:

import pandas as pd

d = dict.fromkeys(['a', 'b', 'c', 'd'])
d['b'] = 'not null'
d['c'] = ''  # empty string

print(d)

# convert `dict` to `Series` and replace any blank strings with `None`;
# use the `.dropna()` method and
# then convert back to a `dict`
d_ = pd.Series(d).replace('', None).dropna().to_dict()

print(d_)
Jeschwar
la source
0

Certaines des méthodes mentionnées ci-dessus ignorent s'il y a des entiers et des flottants avec des valeurs 0 et 0,0

Si quelqu'un veut éviter ce qui précède, vous pouvez utiliser le code ci-dessous (supprime les chaînes vides et les valeurs None du dictionnaire imbriqué et de la liste imbriquée):

def remove_empty_from_dict(d):
    if type(d) is dict:
        _temp = {}
        for k,v in d.items():
            if v == None or v == "":
                pass
            elif type(v) is int or type(v) is float:
                _temp[k] = remove_empty_from_dict(v)
            elif (v or remove_empty_from_dict(v)):
                _temp[k] = remove_empty_from_dict(v)
        return _temp
    elif type(d) is list:
        return [remove_empty_from_dict(v) for v in d if( (str(v).strip() or str(remove_empty_from_dict(v)).strip()) and (v != None or remove_empty_from_dict(v) != None))]
    else:
        return d
dheeraj .A
la source
0

«Comme j'écris actuellement une application de bureau pour mon travail avec Python, j'ai trouvé dans l'application de saisie de données quand il y a beaucoup d'entrées et que certaines ne sont pas obligatoires, l'utilisateur peut donc le laisser vide, à des fins de validation, il est facile de saisir toutes les entrées, puis supprimer la clé vide ou la valeur d'un dictionnaire. Donc, mon code ci-dessus montre comment nous pouvons facilement les supprimer, en utilisant la compréhension du dictionnaire et en conservant l'élément de valeur du dictionnaire qui n'est pas vide. J'utilise Python 3.8.3

data = {'':'', '20':'', '50':'', '100':'1.1', '200':'1.2'}

dic = {key:value for key,value in data.items() if value != ''}

print(dic)

{'100': '1.1', '200': '1.2'}
KokoEfraim
la source
Veuillez mentionner la version python également supportera-t-elle la dernière version?
HaseeB Mir
Votre réponse est actuellement signalée comme étant de mauvaise qualité et peut être supprimée. Veuillez vous assurer que votre réponse contient une explication en dehors de tout code.
Tim Stack le
@TimStack Veuillez recommander la suppression des réponses LQ.
10 Rep
@ 10Rep Je ne recommanderai pas la suppression pour une réponse qui peut fonctionner comme une solution mais qui manque simplement de commentaires descriptifs. Je préfère informer l'utilisateur et lui apprendre à quoi ressemble une meilleure réponse.
Tim Stack le
@HasseB Mir J'utilise le dernier Python 3.8.3
KokoEfraim le
-2

Quelques analyses comparatives:

1. Compréhension de liste recréer dict

In [7]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
   ...: dic = {k: v for k, v in dic.items() if v is not None} 
   1000000 loops, best of 7: 375 ns per loop

2. Compréhension de liste recréer dict en utilisant dict ()

In [8]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
   ...: dic = dict((k, v) for k, v in dic.items() if v is not None)
1000000 loops, best of 7: 681 ns per loop

3. Bouclez et supprimez la clé si v est Aucun

In [10]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
    ...: for k, v in dic.items():
    ...:   if v is None:
    ...:     del dic[k]
    ...: 
10000000 loops, best of 7: 160 ns per loop

Ainsi, la boucle et la suppression sont les plus rapides à 160ns, la compréhension de la liste est deux fois moins lente à ~ 375ns et avec un appel à dict()est à nouveau deux fois moins lente à ~ 680ns.

L'emballage de 3 dans une fonction le ramène à environ 275 ns. Aussi pour moi PyPy était environ deux fois plus rapide que neet python.

Richard Mathie
la source
La boucle et la suppression peuvent également générer une RunTimeError, car il n'est pas valide de modifier un dictionnaire lors de l'itération d'une vue. docs.python.org/3/library/stdtypes.html s4.10.1
Airsource Ltd
ah man ouais ok en python 3 c'est vrai mais pas en python 2.7 car les items retournent une liste, donc vous devez appeler list(dic.items())py 3. Dict compréhension ftw alors? del semble toujours plus rapide pour un faible ratio de valeurs nulles / vides. Je suppose que la construction de cette liste est tout aussi mauvaise pour la consommation de mémoire que la simple recréation du dict.
Richard Mathie