Dictados mezclados con matrices

21

Si realmente necesita modificar el diccionario original:

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

Tenga en cuenta que tenemos que hacer una lista de las claves vacías porque no podemos modificar un diccionario mientras iteramos a través de él (como habrá notado). Sin embargo, esto es menos costoso (en términos de memoria) que crear un diccionario nuevo, a menos que haya muchas entradas con valores vacíos.

neonneo
fuente
esto también eliminará el valor 0 y 0 no está vacío
JVK
2
Si está utilizando Python 3+ tiene que sustituir .iteritems()con .items(), la primera ya no funciona en las versiones más recientes de Python.
Mariano Ruiz

Respuestas:

12

La solución de BrenBarn es ideal (y pitónica, debo agregar). Sin embargo, aquí hay otra solución (fp):

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

Si desea un enfoque con todas las funciones, pero conciso, para manejar estructuras de datos del mundo real que a menudo están anidadas e incluso pueden contener ciclos, le recomiendo mirar la utilidad de reasignación del paquete de utilidades de boltons .

Después de pip install boltonscopiar iterutils.py en su proyecto, simplemente haga lo siguiente:

from boltons.iterutils import remap

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

Esta página tiene muchos más ejemplos, incluidos los que trabajan con objetos mucho más grandes de la API de Github.

Es Python puro, por lo que funciona en todas partes y está completamente probado en Python 2.7 y 3.3+. Lo mejor de todo es que lo escribí exactamente para casos como este, así que si encuentra un caso que no maneja, puede pedirme que lo solucione aquí mismo .

Mahmoud Hashemi
fuente
1
Esta solución funcionó muy bien para un problema similar que tuve: eliminar valores vacíos de listas profundamente anidadas dentro de los diccionarios. ¡Gracias!
Nicholas Tulach
1
Esto es bueno, ya que no está reinventando la rueda y proporcionando una solución para objetos anidados. ¡Gracias!
Vekerdyb
1
Me gustó mucho el artículo que escribiste para tu biblioteca, ¡y esta es una biblioteca útil!
Lifelogger
11

Basado en la solución de Ryan , si también tiene listas y diccionarios anidados:

Para 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

Para 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
fuente
1
¡Qué bonita extensión! Es una buena solución para diccionarios como los siguientes:d = { "things": [{ "name": "" }] }
Ryan Shea
6

Si tiene un diccionario anidado y desea que esto funcione incluso para subelementos vacíos, puede usar una variante recursiva de la sugerencia 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
fuente
Usar en items()lugar de iteritems()para Python 3
andydavies
6

Respuesta rápida (TL; DR)

Example01

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

Respuesta detallada

Problema

  • Contexto: Python 2.x
  • Escenario: el desarrollador desea modificar un diccionario para excluir valores en blanco
    • también conocido como eliminar valores vacíos de un diccionario
    • también conocido como eliminar claves con valores en blanco
    • también conocido como diccionario de filtro para valores que no están en blanco en cada par clave-valor

Solución

  • example01 usa la sintaxis de comprensión de listas de Python con condicional simple para eliminar valores "vacíos"

Trampas

  • example01 solo funciona con una copia del diccionario original (no se modifica en su lugar)
  • example01 puede producir resultados inesperados dependiendo de lo que el desarrollador quiera decir con "vacío"
    • ¿El desarrollador tiene la intención de mantener valores falsos ?
    • Si no se garantiza que los valores en el diccionario sean cadenas, el desarrollador puede tener una pérdida de datos inesperada.
    • result01 muestra que solo se conservaron tres pares clave-valor del conjunto original

Ejemplo alternativo

  • example02 ayuda a lidiar con posibles problemas
  • El enfoque consiste en utilizar una definición más precisa de "vacío" cambiando el condicional.
  • Aquí solo queremos filtrar los valores que se evalúan como cadenas en blanco.
  • Aquí también usamos .strip () para filtrar valores que consisten solo en espacios en blanco.

Ejemplo02

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

Ver también

dreftymac
fuente
5

Para python 3

dict((k, v) for k, v in metadata.items() if v)
Héctor Oliveros
fuente
4

Basándose en las respuestas de patriciasz y nneonneo , y teniendo en cuenta la posibilidad de que desee eliminar claves que solo tienen ciertas cosas falsas (por ejemplo '') pero no otras (por ejemplo 0), o tal vez incluso desee incluir algunas cosas verdaderas (por ejemplo 'SPAM') , entonces podría hacer una lista de resultados muy específica:

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

Desafortunadamente, esto no funciona del todo porque, por ejemplo, se 0 in unwantedevalúa como True. Necesitamos discriminar entre 0otras cosas falsas, así que tenemos que usar is:

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

... evalúa a False.

Ahora utilícelo para dellas cosas no deseadas:

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 desea un diccionario nuevo, en lugar de modificarlo metadataen su lugar:

newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
kwinkunks
fuente
muy buen tiro, aborda muchos problemas a la vez y resuelve la pregunta, gracias por dejarlo en claro
jlandercy
¡Frio! Funciona para este ejemplo. Sin embargo, no funciona cuando un elemento del diccionario es[]
jsga
2

Leí todas las respuestas en este hilo y algunas se refirieron también a este hilo: Eliminar dictados vacíos en el diccionario anidado con función recursiva

Originalmente usé la solución aquí y funcionó muy bien:

Intento 1: demasiado caliente (sin rendimiento ni preparado para el futuro) :

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

Pero se plantearon algunas preocupaciones de rendimiento y compatibilidad en el mundo de Python 2.7:

  1. usar en isinstancelugar detype
  2. Desenrolle la composición de la lista en forbucle para mayor eficiencia
  3. use python3 safe en itemslugar deiteritems

Intento 2: demasiado frío (carece de memorización) :

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! Esto no es recursivo ni memorable en absoluto.

Intento 3: Justo a la derecha (hasta ahora) :

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
Bienaventuranza
fuente
1
a menos que sea ciego, me parece que los intentos 2 y 3 son exactamente iguales ...
luckyguy73
1

Dictados mezclados con matrices

  • La respuesta en el intento 3: Just Right (hasta ahora) de la respuesta de BlissRage no maneja adecuadamente los elementos de las matrices. Incluyo un parche por si alguien lo necesita. El método maneja la lista con el bloque de instrucciones de if isinstance(v, list):, que limpia la lista usando la scrub_dict(d)implementación original .
    @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
fuente
increíble . . .
Hice
0

Una forma alternativa de hacerlo es mediante la comprensión del diccionario. Esto debería ser compatible con2.7+

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

Aquí hay una opción si está utilizando 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
fuente
0

Algunos de los métodos mencionados anteriormente ignoran si hay números enteros y flotan con valores 0 y 0.0

Si alguien quiere evitar lo anterior, puede usar el siguiente código (elimina las cadenas vacías y los valores Ninguno del diccionario anidado y la lista anidada):

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

"Como actualmente también escribo una aplicación de escritorio para mi trabajo con Python, encontré en la aplicación de entrada de datos cuando hay muchas entradas y algunas no son obligatorias, por lo que el usuario puede dejarla en blanco, para fines de validación, es fácil de tomar todas las entradas y luego descartar la clave vacía o el valor de un diccionario. Entonces, mi código anterior muestra cómo podemos eliminarlos fácilmente, usando la comprensión del diccionario y manteniendo el elemento de valor del diccionario que no está en blanco. Yo uso 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
fuente
Por favor, mencione la versión de Python y ¿admitirá la última versión?
HaseeB Mir
Su respuesta está marcada actualmente como de baja calidad. Es posible que se elimine. Asegúrese de que su respuesta contenga una explicación además de cualquier código.
Tim Stack
@TimStack Recomiende la eliminación para las respuestas de LQ.
10 Rep
@ 10Rep No recomendaré la eliminación de una respuesta que puede funcionar como una solución, pero que simplemente carece de comentarios descriptivos. Prefiero informar al usuario y enseñarle cómo es una mejor respuesta.
Tim Stack
@HasseB Mir Uso la última
versión de
-2

Algunas evaluaciones comparativas:

1. Lista de comprensión recrear 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. Lista de comprensión recrear dict usando 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. Repetir y eliminar la tecla si v es Ninguno

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

por lo que repetir y eliminar es el más rápido a 160 ns, la comprensión de la lista es la mitad de lenta a ~ 375 ns y con una llamada a dict()es la mitad de lenta de nuevo a ~ 680 ns.

Envolver 3 en una función lo vuelve a bajar a aproximadamente 275ns. También para mí, PyPy fue aproximadamente el doble de rápido que neet python.

Richard Mathie
fuente
Loop y delete también pueden lanzar un RunTimeError, ya que no es válido modificar un diccionario mientras se itera una vista. docs.python.org/3/library/stdtypes.html s4.10.1
Airsource Ltd
ah hombre sí está bien en python 3 eso es cierto pero no en python 2.7 ya que los elementos devuelven una lista, así que tienes que llamar list(dic.items())en py 3. ¿Dict comprensión ftw entonces? del todavía parece más rápido para una relación baja de valores nulos / vacíos. Supongo que construir esa lista es tan malo para el consumo de memoria que simplemente recrear el dict.
Richard Mathie