dict에서 빈 세트를 제거하는 간단한 방법 (Simple way to remove empty sets from dict)


문제 설명

dict에서 빈 세트를 제거하는 간단한 방법 (Simple way to remove empty sets from dict)

사전에서 컬렉션의 무리를 추적해야 하는 일반적인 문제가 있습니다. 친구에게서 빌린 항목을 추적하고 싶다고 가정해 보겠습니다. defaultdict 클래스는 이 작업을 수행하는 데 매우 유용합니다.

from collections import defaultdict

d = defaultdict(set)
d['Peter'].add('salt')
d['Eric'].add('car')
d['Eric'].add('jacket')

# defaultdict(<class 'set'>, {'Peter': {'salt'}, 'Eric': {'jacket', 'car'}})

이를 통해 키가 이미 세트에 있는지 걱정하지 않고 각 세트에 항목을 추가할 수 있습니다. 이제 내가 소금을 베드로에게 돌려준다면. 이것은 내가 그에게 빚진 것이 없으며 그는 사전에서 제거될 수 있음을 의미합니다. 이렇게 하는 것이 조금 더 번거롭습니다.

d['Peter'].remove('salt')
if not d['Peter']:
    del(d['Peter'])

이것을 함수에 넣을 수 있다는 것을 알고 있지만 가독성을 위해 해당 집합이 비어 있는 경우 키를 자동으로 제거하는 클래스를 원합니다. 이 작업을 수행할 수 있는 방법이 있습니까?

수정

좋습니다. 상속을 사용하여 문제를 해결하고 인덱스 기능을 변경하려고 할 때 이 아이디어에 대한 꽤 중요한 문제를 깨달았습니다. 이것은 d[index]를 호출할 때 값이 .remove(something)를 호출하기 전에 이미 반환된 것이 분명하기 때문에 사전이 그것이 가지고 있다는 것을 알 수 없게 만듭니다. 비워졌다. 다른 것을 사용하는 방법이 정말 없는 것 같아요.


참조 솔루션

방법 1:

A dictionary comprehension might be useful.

from collections import defaultdict

d = defaultdict(set)
d['Peter'].add('salt')
d['Eric'].add('car')
d['Eric'].add('jacket')

d['Peter'].remove('salt')

d2 = {k: v for k, v in d.items() if len(v) > 0}

The d2 dictionary is now:

{'Eric': {'car', 'jacket'}}

Alternatively, using the fact that an empty set is considered false in Python.

d2 = {k: v for k, v in d.items() if v}

Defining a class to implement this logic, similar to the other answer, we can simply ignore keys/values where the value meets a criteria. A function is passed using the ignore parameter to define that criteria.

from collections import defaultdict

class default_ignore_dict(defaultdict):

    def __init__(self, factory, ignore, *args, **kwargs):
        defaultdict.__init__(self, factory, *args, **kwargs)
        self.ignore = ignore

    def __contains__(self, key):
        return defaultdict.__contains__(self, key) and not self.ignore(self[key])

    def items(self):
        return ((k, v) for k, v in defaultdict.items(self) if not self.ignore(v))

    def keys(self):
        return (k for k, _ in self.items())

    def values(self):
        return (v for _, v in self.items())

Testing this:

>>> d = default_ignore_dict(set, lambda s: not s)
>>> d['Peter'].add('salt')
>>> d['Peter'].remove('salt')
>>> d['Eric'].add('car')
>>> d['Eric'].add('jacket')
>>> 
>>> 'Peter' in d
False
>>> list(d.items())
[('Eric', {'car', 'jacket'})]
>>>

방법 2:

The problem with using a defaultdict to do what you want is that even accessing a key sets that key using the factory function. Consider:

from collections import defaultdict

d = defaultdict(set)

if d["Peter"]:
    print("I owe something to Peter")

print(d)
# defaultdict(set, {'Peter': set()})

Also, the problem with creating a sub‑class, as you've realized, the __getitem__() method is called before the set is ever emptied, so you'd have to call another function that checks if the set is empty and remove it.

A better idea might be to just not include keys with empty sets when you're creating the string representation.

class NewDefaultDict(defaultdict):
    def __repr__(self):
        return (f"NewDefaultDict({repr(self.default_factory)}, {{" +
        ", ".join(f"{repr(k)}: {repr(v)}" for k, v in self.items() if v) + 
        "})")

nd = NewDefaultDict(set)
nd["Peter"].add("salt")
nd["Paul"].add("pepper")
nd["Paul"].remove("pepper")

print(nd)
# NewDefaultDict(<class 'set'>, {'Peter': {'salt'}})

You would also need to redefine __contains__() to check if the value is empty, so that e.g. "Paul" in nd returns False:

    def __contains__(self, key):
        return defaultdict.__contains__(self, key) and self[key]

To make it compatible with for ... in nd constructs and dict‑unpacking, you can redefine __iter__():

    def __iter__(self):
        for key in defaultdict.__iter__(self):
            if self[key]: yield key

Then,

for k in nd:
    print(k)

gives:

Peter

(by iHnRChrisPranav Hosangadi)

참조 문서

  1. Simple way to remove empty sets from dict (CC BY‑SA 2.5/3.0/4.0)

#defaultdict #Python #Dictionary






관련 질문

많이 중첩 된 defaultdict에서 물건을 계산하는보다 Pythonic 방법 (More Pythonic way of counting things in a heavily nested defaultdict)

GAE ndb에서 산세척 (Pickling on GAE ndb)

파이썬을 사용하여 구분된 문자열 목록을 트리/중첩 사전으로 변환 (convert a list of delimited strings to a tree/nested dict, using python)

Python에서 defaultdict 또는 dict를 Ordereddict로 변환할 수 있습니까? (Can I convert a defaultdict or dict to an ordereddict in Python?)

기본 사전의 각 키를 고유한 CSV 파일에 쓰기 (Writing each key in a default dict to a unique csv file)

TypeError: 첫 번째 인수는 호출 가능해야 합니다. defaultdict (TypeError: first argument must be callable, defaultdict)

defaultdict(list)를 Pandas DataFrame으로 변환하는 방법 (How to convert a defaultdict(list) to Pandas DataFrame)

여러 매개변수가 있는 defaultdict (defaultdict with multiple parameters)

함께 사용되는 Defaultdict 및 람다 함수 (Defaultdict and lambda function used together)

클래스/유형을 포함하지 않고 중첩된 defaultdict를 인쇄하는 방법은 무엇입니까? (How to print a nested defaultdict without including the class/type?)

dict에서 빈 세트를 제거하는 간단한 방법 (Simple way to remove empty sets from dict)

특정 키를 포함하는 많은 defaultdicts의 값 가져오기 (Getting the values of many defaultdicts that contains a certain key)







코멘트