Mit Arrays gemischte Diktate

21

Wenn Sie das Originalwö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 wir es durchlaufen (wie Sie vielleicht bemerkt haben). Dies ist jedoch kostengünstiger (speichertechnisch) als das Erstellen eines brandneuen Wörterbuchs, es sei denn, es gibt viele Einträge mit leeren Werten.

nneonneo
quelle
Dadurch
2
Wenn Sie Python verwenden 3+ Sie ersetzen müssen .iteritems()mit .items(), wird der erste nicht mehr funktioniert in neuesten Python - Versionen.
Mariano Ruiz

Antworten:

12

Die Lösung von BrenBarn ist ideal (und pythonisch, könnte ich hinzufügen). Hier ist jedoch eine andere (fp) Lösung:

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

Wenn Sie einen umfassenden und dennoch prägnanten Ansatz für den Umgang mit realen Datenstrukturen wünschen, die häufig verschachtelt sind und sogar Zyklen enthalten können, empfehlen wir Ihnen, das Dienstprogramm remap aus dem Dienstprogramm von bolons zu betrachten .

Nach pip install boltonsoder Kopieren iterutils.py in Ihr Projekt, nur tun:

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 reines Python, funktioniert also überall und ist in Python 2.7 und 3.3+ vollständig getestet. Das Beste von allem ist, dass ich es für genau solche Fälle geschrieben habe. Wenn Sie also einen Fall finden, der nicht funktioniert, können Sie mich nerven, um ihn hier zu beheben .

Mahmoud Hashemi
quelle
1
Diese Lösung funktionierte hervorragend für ein ähnliches Problem: Leere Werte aus tief verschachtelten Listen in Wörterbüchern entfernen. Vielen Dank!
Nicholas Tulach
1
Dies ist gut, da Sie das Rad nicht neu erfinden und eine Lösung für verschachtelte Objekte bereitstellen. Vielen Dank!
Vekerdyb
1
Der Artikel, den Sie für Ihre Bibliothek geschrieben haben, hat mir sehr gut gefallen, und dies ist eine nützliche Bibliothek!
Lebenslogger
11

Basierend auf Ryans Lösung , wenn 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
Chachan
quelle
1
Ha, schöne Erweiterung! Es ist eine gute Lösung für Wörterbücher wie die folgenden:d = { "things": [{ "name": "" }] }
Ryan Shea
6

Wenn Sie ein verschachteltes Wörterbuch haben und möchten, dass dies auch für leere Unterelemente funktioniert, können Sie eine rekursive Variante von BrenBarns Vorschlag 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
Ryan Shea
quelle
Verwenden Sie items()anstelle von iteritems()für Python 3
andydavies
6

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

Detaillierte Antwort

Problem

  • Kontext: Python 2.x.
  • Szenario: Entwickler möchten ein Wörterbuch so ändern, dass leere Werte ausgeschlossen werden
    • aka leere Werte aus einem Wörterbuch entfernen
    • aka Löschen von Schlüsseln mit leeren Werten
    • aka Filterwörterbuch für nicht leere Werte über jedes Schlüssel-Wert-Paar

Lösung

  • Beispiel01 Verwenden Sie die Python-Listenverständnis-Syntax mit einer einfachen Bedingung, um "leere" Werte zu entfernen

Tücken

  • example01 bearbeitet nur eine Kopie des Originalwörterbuchs (wird nicht geändert)
  • Beispiel01 kann zu unerwarteten Ergebnissen führen, je nachdem, was Entwickler unter "leer" versteht.
    • Bedeutet Entwickler, Werte zu behalten, die falsch sind ?
    • Wenn nicht garantiert wird, dass die Werte im Wörterbuch Zeichenfolgen sind, kann es beim Entwickler zu unerwarteten Datenverlusten kommen.
    • Ergebnis01 zeigt, dass nur drei Schlüssel-Wert-Paare aus dem ursprünglichen Satz erhalten blieben

Alternatives Beispiel

  • Beispiel 02 hilft bei der Bewältigung potenzieller Fallstricke
  • Der Ansatz besteht darin, eine genauere Definition von "leer" zu verwenden, indem die Bedingung geändert wird.
  • 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.

Beispiel 02

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

dreftymac
quelle
5

Für Python 3

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

Aufbauend auf den Antworten von patriciasz und nneonneo , und die Möglichkeit entfallen , dass Sie die Schlüssel löschen möchten , dass nur bestimmte falsy Dinge (zB '') , aber nicht andere (zB 0), oder vielleicht wollen Sie auch einige truthy Dinge enthalten (zB 'SPAM') Dann könnten Sie eine hochspezifische Hitliste erstellen:

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

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

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

... bewertet zu False.

Verwenden Sie es jetzt für deldie unerwünschten Dinge:

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 möchten, anstatt es zu ändern metadata:

newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
Kwinkunks
quelle
wirklich schöner Schuss, es spricht viele Probleme gleichzeitig an und es löst die Frage, danke, um es klar zu machen
jlandercy
Cool! Es funktioniert für dieses Beispiel. Es funktioniert jedoch nicht, wenn ein Element im Wörterbuch ist[]
jsga
2

Ich habe alle Antworten in diesem Thread gelesen und einige haben auch auf diesen Thread verwiesen: Entfernen Sie leere Diktate im verschachtelten Wörterbuch mit rekursiver Funktion

Ich habe hier ursprünglich eine Lösung verwendet und es hat großartig 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 isinstancestatttype
  2. Rollen Sie die Liste comp foraus Effizienzgründen in einer Schleife ab
  3. Verwenden Sie itemsstattdessen Python3 Safeiteritems

Versuch 2: Zu kalt (ohne Auswendiglernen) :

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 überhaupt nicht 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
BlissRage
quelle
1
es sei denn, ich bin blind, es
scheint
1

Mit Arrays gemischte Diktate

  • Die Antwort bei Versuch 3: Genau richtig (bisher) von BlissRages Antwort behandelt Array-Elemente nicht richtig. Ich füge einen Patch hinzu, falls jemand ihn braucht. Die Methode behandelt Listen mit dem Anweisungsblock von if isinstance(v, list):, der die Liste mit der ursprünglichen scrub_dict(d)Implementierung bereinigt .
    @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
quelle
genial . . . Ich hatte diese Änderung in der Codebasis vorgenommen, aber Ihren Kommentar verpasst _ / _
BlissRage
0

Eine alternative Möglichkeit besteht darin, das Wörterbuchverständnis zu verwenden. Dies sollte kompatibel sein mit2.7+

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

Hier ist eine Option, wenn Sie verwenden 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
quelle
0

Einige der oben genannten Methoden ignorieren, ob Ganzzahlen vorhanden sind, und schweben mit den Werten 0 und 0,0

Wenn jemand das oben Genannte vermeiden möchte, kann er den folgenden Code verwenden (entfernt leere Zeichenfolgen und keine 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
dheeraj .A
quelle
0

"Da ich derzeit auch eine Desktop-Anwendung für meine Arbeit mit Python schreibe, habe ich in einer Dateneingabeanwendung festgestellt, dass viele Eingaben vorhanden sind und einige nicht obligatorisch sind, sodass der Benutzer sie leer lassen kann. Zu Validierungszwecken ist sie leicht zu greifen alle Einträge und verwerfen dann den leeren Schlüssel oder Wert eines Wörterbuchs. Mein Code über a zeigt also, wie wir sie einfach herausnehmen können, indem wir das Wörterbuchverständnis verwenden und das Wörterbuchwertelement behalten, das nicht leer ist. Ich verwende 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
quelle
Bitte erwähnen Sie die Python-Version. Unterstützt sie auch die neueste Version?
HaseeB Mir
Ihre Antwort ist derzeit als minderwertig gekennzeichnet und wird möglicherweise gelöscht. Bitte stellen Sie sicher, dass Ihre Antwort neben jedem Code eine Erklärung enthält.
Tim Stack
@ TimStack Bitte empfehlen Sie das Löschen für LQ-Antworten.
10 Rep
@ 10Rep Ich empfehle nicht das Löschen für eine Antwort, die als Lösung funktioniert, aber lediglich keine beschreibenden Kommentare enthält. Ich möchte den Benutzer lieber informieren und ihm beibringen, wie eine bessere Antwort aussieht.
Tim Stack
@HasseB Mir Ich benutze die neueste Python 3.8.3
KokoEfraim
-2

Einige Benchmarking:

1. Listenverständnis neu erstellen Diktat

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. Listenverständnis Diktat neu erstellen mit 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. Schleife und Löschschlüssel, wenn v Keine ist

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

So ist Loop and Delete bei 160 ns am schnellsten, das Listenverständnis ist bei ~ 375 ns halb so langsam und bei einem Aufruf von dict()ist es wieder halb so langsam ~ 680 ns.

Wenn Sie 3 in eine Funktion einwickeln, wird sie wieder auf etwa 275 ns reduziert. Auch für mich war PyPy etwa doppelt so schnell wie Neet Python.

Richard Mathie
quelle
Durch Schleifen und Löschen kann auch ein RunTimeError ausgelöst werden, da das Ändern eines Wörterbuchs während der Iteration einer Ansicht nicht gültig ist. docs.python.org/3/library/stdtypes.html s4.10.1
Airsource Ltd
ah man yeah ok in Python 3 ist das wahr, aber nicht in Python 2.7, da Elemente eine Liste zurückgeben, also müssen Sie list(dic.items())Py 3 aufrufen. del scheint für ein niedriges Verhältnis von Null / Leer-Werten immer noch schneller zu sein. Ich denke, das Erstellen dieser Liste ist für den Speicherverbrauch genauso schlecht wie das Neuerstellen des Diktats.
Richard Mathie