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

patriciasznneonneo 의 답변을 바탕으로 특정 허위 항목 만 포함하고 (예 :) 다른 항목은 포함 ''하지 않는 키를 삭제 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.