dict에서 빈 문자열로 키를 제거하는 효율적인 방법


116

사전이 있고 빈 값 문자열이있는 모든 키를 제거하고 싶습니다.

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

이를 수행하는 가장 좋은 방법은 무엇입니까?

답변:


194

파이썬 2.X

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

파이썬 2.7-3.X

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

모든 키에는 값이 있습니다. 그 값 중 일부는 빈 문자열입니다. 딕셔너리에 값이없는 키 같은 것은 없습니다. 값이 없다면 dict에 없을 것입니다.


29
+1. 이것이 실제로 기존 사전에서 키를 제거하지는 않는다는 점에 유의해야합니다. 오히려 새 사전을 만듭니다. 일반적으로 이것은 정확히 누군가가 원하는 것이고 아마도 OP에 필요한 것이지만 OP가 요구 한 것은 아닙니다.
— Steven Rumbalski 2012 년

18
이것은 또한 v = 0을 죽입니다. 그것이 원하는 경우 괜찮습니다.
— Paul

2
이것은 또한 v = False를 제거하는데, 이는 OP가 요청한 것과 정확히 다릅니다 .
— Amir

4
@shredding : 당신은 의미 .items()합니다.
— BrenBarn

6
이후 버전의 파이썬의 경우 사전 생성기를 사용해야합니다.{k: v for k, v in metadata.items() if v is not None}
— Schiavini

75

BrenBarn의 솔루션 보다 더 짧아 질 수 있습니다 (그리고 더 읽기 쉽습니다 )

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

Python 2.7.3으로 테스트되었습니다.


13
이것은 또한 0 값을 죽입니다.
— Paul

10
당신이 사용할 수있는 0을 유지하려면 ... if v!=None:과 같이 {k: v for k, v in metadata.items() if v!=None}
— Dannid

1
{k : v for k, v in metadata.items () if v! = None}은 빈 문자열을 제거하지 않습니다.
— philgo20

1
사전 이해는 이전 버전과의 호환성을 위해 Python 2.7+에서만 지원됩니다. @BrenBarn의 솔루션을 사용하십시오.
— Pavan Gupta

12
항상 None을 '! ='대신 'is not'과 비교해야합니다. stackoverflow.com/a/14247419/2368836
— rocktheartsm4l

21

원래 사전을 수정해야하는 경우 :

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

딕셔너리를 반복하는 동안에는 딕셔너리를 수정할 수 없기 때문에 빈 키 목록을 만들어야합니다 (알다시피). 값이 비어있는 항목이 많은 경우를 제외하고는 새로운 사전을 만드는 것보다 비용이 적게 듭니다 (메모리 측면에서).


이것은 또한 값 0을 제거하고 0은 비어 있지 않습니다
— JVK

2
Python 3 이상을 사용하는 경우으로 대체 .iteritems()해야 .items()합니다. 첫 번째는 최신 Python 버전에서 더 이상 작동하지 않습니다.
— Mariano Ruiz

12

BrenBarn의 솔루션 은 이상적입니다 (그리고 pythonic, 추가 할 수도 있습니다). 그러나 다음은 또 다른 (fp) 솔루션입니다.

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

12

자주 중첩되고 주기도 포함 할 수있는 실제 데이터 구조를 처리하는 데 완전한 기능을 제공하면서도 간결한 접근 방식을 원한다면 boltons 유틸리티 패키지에서 remap 유틸리티를 살펴 보는 것이 좋습니다 .

iterutils.py 를 프로젝트에 pip install boltons복사 한 후 다음을 수행하십시오.

from boltons.iterutils import remap

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

이 페이지 에는 Github의 API에서 훨씬 더 큰 객체로 작업하는 예제를 포함하여 더 많은 예제가 있습니다.

순수 Python이므로 어디서나 작동하며 Python 2.7 및 3.3 이상에서 완전히 테스트되었습니다. 무엇보다 정확히 이와 같은 경우에 대해 작성 했으므로 처리되지 않는 경우를 발견하면 여기에서 바로 수정하도록 저를 괴롭힐 수 있습니다 .


1
이 솔루션은 내가 가진 유사한 문제에 대해 훌륭하게 작동했습니다. 사전 내부에 깊게 중첩 된 목록에서 빈 값을 제거하는 것입니다. 감사!
— Nicholas Tulach

1
휠을 재발 명하지 않고 중첩 된 오브젝트에 대한 솔루션을 제공하지 않기 때문에 이것은 좋습니다. 감사!
— vekerdyb

1
나는 당신이 당신의 도서관에 쓴 기사가 정말 마음에 들었고 이것은 유용한 도서관입니다!
— lifelogger

11

를 기반으로 라이언의 솔루션 , 당신은 또한 목록 및 중첩 된 사전이있는 경우 :

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

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

1
하, 멋진 확장! 그것은 다음과 같이 사전에 대한 좋은 솔루션입니다 :d = { "things": [{ "name": "" }] }
— 라이언 시어

6

중첩 된 사전이 있고 빈 하위 요소에 대해서도 작동하도록하려면 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

사용 items()대신 iteritems()파이썬 3
— andydavies

6

빠른 답변 (TL; DR)

예 01

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

자세한 답변

문제

  • 컨텍스트 : Python 2.x
  • 시나리오 : 개발자가 사전을 수정하여 빈 값을 제외하려고합니다.
    • 일명 사전에서 빈 값 제거
    • 일명 빈 값이있는 키 삭제
    • 각 키-값 쌍의 공백이 아닌 값에 대한 필터 사전

해결책

  • example01은 "빈"값을 제거하기 위해 간단한 조건부와 함께 파이썬 목록 이해 구문을 사용합니다.

함정

  • example01은 원본 사전의 복사본에서만 작동합니다 (제자리에서 수정하지 않음).
  • example01은 개발자가 "비어 있음"이 의미하는 바에 따라 예기치 않은 결과를 생성 할 수 있습니다.
    • 개발자는 거짓된 값을 유지한다는 의미 입니까?
    • 사전의 값이 문자열로 보장되지 않는 경우 개발자는 예기치 않은 데이터 손실을 경험할 수 있습니다.
    • result01은 원래 세트에서 세 개의 키-값 쌍만 보존되었음을 보여줍니다.

대체 예

  • example02는 잠재적 인 함정을 처리하는 데 도움이됩니다.
  • 접근 방식은 조건을 변경하여 "비어 있음"의보다 정확한 정의를 사용하는 것입니다.
  • 여기서는 빈 문자열로 평가되는 값만 필터링하려고합니다.
  • 여기서도 .strip ()을 사용하여 공백으로 만 구성된 값을 필터링합니다.

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

또한보십시오



4

patriciasz 및 nneonneo 의 답변을 바탕으로 특정 허위 항목 만 포함하고 (예 :) 다른 항목은 포함 ''하지 않는 키를 삭제 0하거나 (예 : 일부 진실 된 항목을 포함 할 수도 있음 'SPAM') (예 :) , 그러면 매우 구체적인 히트리스트를 만들 수 있습니다.

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

불행하게도,이 꽤 있기 때문에 예를 들어, 작동하지 않습니다 0 in unwanted평가 True. 우리는 0다른 가짜 를 구별해야 하므로 다음을 사용해야합니다 is.

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

...로 평가됩니다 False.

이제 del원치 않는 것들에 사용하십시오 .

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]

metadata제자리에서 수정 하는 대신 새 사전을 원하는 경우 :

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

정말 좋은 기회, 당신이 그것을 명확하게하기 위해 감사하고 문제를 해결 한 번에 여러 문제를 해결
— jlandercy

멋있는! 이 예에서 작동합니다. 그러나 사전의 항목이 다음과 []
— 같으면

2

이 스레드의 모든 응답을 읽었으며 일부는이 스레드를 참조 했습니다. 재귀 함수를 사용하여 중첩 된 사전에서 빈 dicts 제거

나는 원래 여기에 솔루션을 사용했으며 훌륭하게 작동했습니다.

시도 1 : 너무 뜨겁다 (성능이 없거나 미래를 보장하지 않음) :

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

그러나 Python 2.7 세계에서 일부 성능 및 호환성 문제가 제기되었습니다.

  1. isinstance대신 사용type
  2. for효율성을 위해 목록 comp를 루프 로 펼칩니다.
  3. items대신 python3 안전을 사용하십시오.iteritems

시도 2 : 너무 차가움 (메모리 부족) :

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! 이것은 재귀 적이 지 않으며 전혀 기억하지 않습니다.

시도 3 : Just Right (지금까지) :

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

1
내가 장님이 아니라면, 시도 2와 3은 정확히 똑같은 것 같습니다 ...
— luckyguy73

1

배열과 혼합 된 사전

  • 에 대답 시도 3 : 그냥 오른쪽 (지금까지) 에서 BlissRage의 대답은 제대로 배열 요소를 처리하지 않습니다. 누군가가 필요할 경우를 대비하여 패치를 포함하고 있습니다. 이 메서드는 if isinstance(v, list):원래 scrub_dict(d)구현을 사용하여 목록을 제거하는 문 블록이있는 목록을 처리합니다 .
    @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

대박 . . . 나는이 코드베이스를 변경했지만 귀하의 의견을 놓쳤습니다. _ / _
— BlissRage

0

이를 수행 할 수있는 또 다른 방법은 사전 이해를 사용하는 것입니다. 이것은 다음과 호환되어야합니다.2.7+

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

0

다음을 사용하는 경우 옵션이 있습니다 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_)

0

위에서 언급 한 일부 메서드는 정수와 값이 0 및 0.0 인 부동 소수점이있는 경우 무시합니다.

누군가 위의 코드를 피하려면 아래 코드를 사용할 수 있습니다 (중첩 된 사전 및 중첩 된 목록에서 빈 문자열 및 없음 값 제거).

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

"현재 Python 작업을위한 데스크톱 응용 프로그램을 작성하고 있기 때문에 데이터 입력 응용 프로그램에서 많은 항목이 있고 일부는 필수 사항이 아니므로 사용자가 유효성 검사를 위해 비워 둘 수 있습니다. 모든 항목을 입력 한 다음 사전의 빈 키 또는 값을 버립니다. 그래서 위의 코드는 사전 이해를 사용하여 쉽게 꺼내고 공백이 아닌 사전 값 요소를 유지하는 방법을 보여줍니다. 나는 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'}

파이썬 버전도 최신 버전을 지원합니까?
— HaseeB Mir

귀하의 답변은 현재 낮은 품질로 표시되어 있으며 삭제 될 수 있습니다. 답변에 코드 외에 설명이 포함되어 있는지 확인하십시오.
— Tim Stack

@TimStack LQ 답변 삭제를 권장합니다.
— 10 회

@ 10Rep 해결책으로 작동 할 수 있지만 설명이없는 답변에 대해서는 삭제를 권장하지 않습니다. 차라리 사용자에게 알리고 더 나은 답변이 무엇인지 알려주고 싶습니다.
— Tim Stack

@HasseB Mir 저는 최신 Python 3.8.3을 사용합니다
— KokoEfraim

-2

일부 벤치마킹 :

1. 목록 이해력 재생성 사전

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. dict ()를 사용하여 목록 이해력 재생성 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. v가 None이면 키 반복 및 삭제

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

따라서 루프 및 삭제는 160ns에서 가장 빠르며 목록 이해는 ~ 375ns에서 절반 정도 느리고 dict() 하면 다시 ~ 680ns에서 절반 정도 느립니다.

3을 함수로 감싸면 다시 약 275ns로 줄어 듭니다. 또한 나에게 PyPy는 neet python보다 약 두 배 빠릅니다.


루프 및 삭제는 뷰를 반복하는 동안 사전을 수정하는 것이 유효하지 않기 때문에 RunTimeError를 throw 할 수도 있습니다. docs.python.org/3/library/stdtypes.html s4.10.1
— Airsource Ltd

아 man yeah ok python 3에서는 사실이지만 항목이 목록을 반환하므로 python 2.7에서는 그렇지 않으므로 list(dic.items())py 3 을 호출해야하므로 Dict comprehension ftw? del은 Null / 빈 값의 낮은 비율에 대해 여전히 더 빠릅니다. 나는 그 목록을 작성하는 것이 단지 dict를 다시 만드는 것보다 메모리 소비에 좋지 않다고 생각합니다.
— Richard Mathie 2017
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.