it-swarm.com.de

Eine effiziente Methode zum Entfernen von Schlüsseln mit leeren Zeichenfolgen aus einem Diktat

Ich habe ein Diktier und möchte alle Schlüssel entfernen, für die leere Wertzeichenfolgen vorhanden sind.

metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
            u'EXIF:CFAPattern2': u''}

Was ist der beste Weg, dies zu tun?

86
ensnare

Python 2.X

dict((k, v) for k, v in metadata.iteritems() if v)

Python 2.7 - 3.X

{k: v for k, v in metadata.items() if v is not None}

Beachten Sie, dass alle Ihre Schlüssel Werte haben. Es ist nur so, dass einige dieser Werte die leere Zeichenfolge sind. Es gibt keinen Schlüssel in einem Diktier ohne Wert. Wenn es keinen Wert hätte, wäre es nicht im Dikt.

143
BrenBarn

Es kann sogar noch kürzer sein als BrenBarns Lösung (und lesbarer, denke ich)

{k: v for k, v in metadata.items() if v}

Mit Python getestet 2.7.3.

67
patriciasz

Wenn Sie das ursprüngliche Wörterbuch wirklich ändern müssen:

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

Beachten Sie, dass wir eine Liste der leeren Schlüssel erstellen müssen, da wir ein Wörterbuch nicht ändern können, während Sie es durchlaufen (wie Sie vielleicht bemerkt haben). Dies ist jedoch weniger kostspielig als das Erstellen eines brandneuen Wörterbuchs, es sei denn, es gibt viele Einträge mit leeren Werten.

19
nneonneo

Wenn Sie einen umfassenden und dennoch prägnanten Ansatz für den Umgang mit realen Datenstrukturen wünschen, die oft verschachtelt sind und sogar Zyklen enthalten können, empfehle ich Ihnen, das Remap-Dienstprogramm aus dem Dienstprogramm boltons zu betrachten. 

Nachdem Sie pip install boltons oder iterutils.py in Ihr Projekt kopiert haben, tun Sie einfach:

from boltons.iterutils import remap

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

Diese Seite enthält viele weitere Beispiele, einschließlich solcher, die mit viel größeren Objekten aus der Github-API arbeiten.

Es ist pure Python, es funktioniert also überall und ist in Python 2.7 und 3.3+ vollständig getestet. Das Beste von allem ist, ich habe es für genau solche Fälle geschrieben. Wenn Sie einen Fall finden, mit dem er nicht zurechtkommt, können Sie mich dazu veranlassen, ihn zu beheben rechts hier .

11
Mahmoud Hashemi

BrenBarns Lösung ist ideal (und Pythonic kann ich hinzufügen). Hier ist jedoch eine andere (fp) Lösung:

from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
8
Joel Cornett

Basierend auf Ryans Lösung , falls Sie auch Listen und verschachtelte Wörterbücher haben:

Für 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

Für 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
8
chachan

Wenn Sie ein verschachteltes Wörterbuch haben und dies auch für leere Unterelemente funktionieren soll, können Sie eine rekursive Variante des BrenBarn-Vorschlags verwenden:

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
6
Ryan

Ausgehend von den Antworten aus patriciasz und nneonneo und der Berücksichtigung der Möglichkeit, dass Sie möglicherweise Schlüssel löschen möchten, die nur bestimmte falsche Dinge (z. B. '') enthalten, nicht jedoch andere (z. B. 0) oder Sie Wenn Sie sogar einige wahrheitsgemäße Dinge hinzufügen möchten (z. B. 'SPAM'), können Sie eine sehr spezifische Trefferliste erstellen:

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

Leider funktioniert das nicht ganz, weil zum Beispiel 0 in unwanted zu True ausgewertet wird. Wir müssen zwischen 0 und anderen falschen Dingen unterscheiden, also müssen wir is verwenden:

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

... wird zu False ausgewertet.

Jetzt del die unerwünschten Dinge verwenden:

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]

Wenn Sie ein neues Wörterbuch wünschen, anstatt metadata an Ort und Stelle zu ändern:

newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
4
kwinkunks

Schnelle Antwort (TL; DR)

Beispiel01

### 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'}
'''

Ausführliche Antwort

Problem

  • Context: Python 2.x
  • Szenario: Der Entwickler möchte ein Wörterbuch so ändern, dass leere Werte ausgeschlossen werden
    • aka entfernt leere Werte aus einem Wörterbuch
    • aka Löschtasten mit leeren Werten
    • aka Filterwörterbuch für nicht leere Werte für jedes Schlüsselwertpaar

Lösung

  • example01 verwendet Python-Listenverständnissyntax mit einfachen Bedingungen, um "leere" Werte zu entfernen

Fallstricke

  • example01 bearbeitet nur eine Kopie des Originalwörterbuchs (ändert sich nicht an Ort und Stelle)
  • example01 kann zu unerwarteten Ergebnissen führen, je nachdem, was Entwickler als "leer" bezeichnet
    • Bedeutet der Entwickler, dass Werte beibehalten werden, die falsch sind ?
    • Wenn die Werte im Wörterbuch nicht als Zeichenfolgen definiert werden, kann der Entwickler unerwarteten Datenverlust erleiden.
    • result01 zeigt, dass nur drei Schlüssel-Wert-Paare aus dem ursprünglichen Satz erhalten wurden

Alternatives Beispiel

  • example02 hilft bei der Behandlung potenzieller Fallstricke
  • Der Ansatz besteht darin, eine genauere Definition von "leer" zu verwenden, indem die Bedingungen geändert werden.
  • Hier möchten wir nur Werte herausfiltern, die zu leeren Zeichenfolgen ausgewertet werden.
  • Hier verwenden wir auch .strip (), um Werte herauszufiltern, die nur aus Leerzeichen bestehen. 

Beispiel02

### 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'
  }
'''

Siehe auch

4
dreftymac

Für Python 3

dict((k, v) for k, v in metadata.items() if v)
3
Hector Oliveros

Eine alternative Möglichkeit, dies zu tun, ist die Verwendung des Wörterbuchverständnisses. Dies sollte mit 2.7+ kompatibel sein.

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

Ich habe alle Antworten in diesem Thread gelesen und einige verwiesen auch auf diesen Thread: Leere Wörter in verschachtelten Wörterbüchern mit rekursiver Funktion entfernen

Ich habe hier ursprünglich eine Lösung verwendet und es hat super funktioniert:

Versuch 1: zu heiß (nicht performant oder zukunftssicher)}:

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

In der Python 2.7-Welt wurden jedoch einige Bedenken hinsichtlich Leistung und Kompatibilität geäußert:

  1. verwenden Sie isinstance anstelle von type
  2. roll die Liste comp in for Schleife aus Effizienzgründen ab
  3. verwenden Sie python3 safe items anstelle von iteritems

Versuch 2: zu kalt (keine Memoisierung):

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! Dies ist nicht rekursiv und in keiner Weise memoizant.

Versuch 3: genau richtig (bisher):

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
0
BlissRage

Hier ist eine Option, wenn Sie pandas verwenden:

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_)
0
jeschwar

Einige der oben genannten Methoden werden ignoriert, wenn Ganzzahlen und Gleitkommazahlen mit den Werten 0 und 0,0 vorhanden sind

Wenn jemand vermeiden möchte, dass der obige Code unten verwendet wird (entfernt leere Zeichenfolgen und None-Werte aus dem verschachtelten Wörterbuch und der verschachtelten Liste):

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
0
dheeraj .A