The problem with implementing this using a set comprehension as you do is that an O(1) operation is turned into an O(N) operation, since you need to check item.casefold() != unwanted_String.casefold()
for each item
in the set.
One option to work around this would be to keep a dictionary that stores the strings in a set with a lowercased key. When you want to delete an element, find all elements that have the same lowercase value, and delete those too.
You could write a class to handle this that would look like so:
class EasyRemoveSet(set):
def __init__(self, *args, key_func=str.casefold, **kwargs):
super().__init__(*args, **kwargs)
self.__key_func = key_func
self.__lookup = {}
self.__add_to_lookup(self)
def __add_to_lookup(self, elems):
for elem in elems:
self.__lookup.setdefault(self.__key_func(elem), set()).add(elem)
def add(self, elem):
super().add(elem)
self.__add_to_lookup([elem])
def remove(self, elem):
elems_to_remove = self.__lookup.pop(self.__key_func(elem))
for e in elems_to_remove:
super().remove(e)
def discard(self, elem):
elems_to_remove = self.__lookup.pop(self.__key_func(elem), [])
for e in elems_to_remove:
super().discard(e)
def clear(self):
super().clear()
self.__lookup = {}
Then, you can do:
setA = EasyRemoveSet(["abc", "Abc", "def", "DeF", "ABC", "abC", "DEF", "abc"])
print(setA) # EasyRemoveSet({'abc', 'DEF', 'DeF', 'ABC', 'abC', 'def', 'Abc'})
setA.remove("Abc")
print(setA) # EasyRemoveSet({'DEF', 'DeF', 'def'})
The keyword-only argument key_func
allows you to specify a callable whose return value will be used as the key to identify duplicates. For example, if you wanted to use this class for integers, and remove negative and positive integers in one go:
num_set = EasyRemoveSet([1, 2, 3, 4, 5, -1, -2, -3, -4, -5], key_func=abs)
print(num_set)
# EasyRemoveSet({1, 2, 3, 4, 5, -2, -5, -4, -3, -1})
num_set.discard(-5)
print(num_set)
# EasyRemoveSet({1, 2, 3, 4, -2, -4, -3, -1})