What a hash set is, and what it is called in Python
A hash set is a collection of unique values with very fast insertion and lookup. If you add a value that is already inside, nothing changes, so no value appears twice. Python has no class called HashSet: its built-in set type is a hash set. When people search for a "hashset in Python", the answer is set.
It is fast because it stores each value at a position computed from the value's hash. To test whether a value is present, Python computes the hash and looks at that position instead of scanning every element. Adding, removing and checking take O(1) time on average, while checking whether a value is in a list takes O(n).
Use a set when you need to remove duplicates, check membership quickly, or compare groups of values. A set does not keep an order and cannot be indexed, so my_set[0] is an error.
Creating a set
An empty set
Call set() with no arguments. Empty curly braces do not work: {} creates an empty dictionary.
emptySet = set()
print(type(emptySet)) # <class 'set'>
print(type({})) # <class 'dict'>
From a list or another iterable
Pass any iterable to set() and the duplicates disappear. This takes O(n) time for n elements:
elements = [1, '2', 'apple', 1]
mySet = set(elements)
print(len(mySet)) # 3
print(set("hello") == {'h', 'e', 'l', 'o'}) # True
With curly braces
When you already know the values, list them in braces:
dataEngineer = {'Python', 'Java', 'Scala', 'Git', 'SQL', 'Hadoop'}
Adding, checking and removing
mySet = {'a', 'b', 'c'}
mySet.add('d') # added
mySet.add('a') # already there, nothing happens
print('a' in mySet) # True
print('z' in mySet) # False
print(len(mySet)) # 4
add(x)insertsxif it is missing and does nothing, without an error, if it is already there. To add several values at once, useupdate([...]).x in sis the membership test, andx not in sis its opposite.remove(x)deletesxand raisesKeyErrorwhen it is missing.discard(x)deletesxand does nothing when it is missing. It is the safer choice when you don't know whether the value is there.
mySet = {'a', 'b', 'c', 'd'}
mySet.discard('a') # removes 'a'
mySet.discard('z') # nothing happens
mySet.remove('z') # KeyError: 'z'
Looping over a set
A for loop visits each value once, in no order you can rely on. For small integers the order often looks sorted, and for strings it can change from one run of the program to the next. If you need a stable order, loop over sorted(mySet).
mySet = {'b', 'c', 'c', 'd'}
for val in mySet:
print(val)
print(sorted(mySet)) # ['b', 'c', 'd']
The loop prints b, c and d in some order, and c only once.
What can go in a set
Every element of a set must be hashable. Numbers, strings, booleans and tuples of such values qualify. Lists, dictionaries and other sets do not, because they can change after they are added:
print({(1, 2), (1, 2)}) # {(1, 2)}
{[1, 2]} # TypeError: unhashable type: 'list'
To put a set inside another set, use a frozenset, an immutable version: frozenset([1, 2]).
The lesson's exercise, step by step
- Make a list with a duplicate.
fruitsArrayholds'apples','bananas'and'apples'. A list keeps all three. - Convert it to a set.
fruits = set(fruitsArray)keeps two values, because the second'apples'is a duplicate. - Add a new value.
fruits.add('oranges')grows the set to three values. - Check for a value.
'apples' in fruitsis true, so the program printsWe have apples. - Remove a value.
fruits.discard('bananas')leaves'apples'and'oranges'. - Print every value with a
forloop overfruits, using the loop variablefruit.
fruitsArray = ['apples', 'bananas', 'apples']
fruits = set(fruitsArray)
fruits.add('oranges')
if 'apples' in fruits:
print('We have apples')
fruits.discard('bananas')
for fruit in fruits:
print(fruit)
The program prints We have apples, then apples and oranges in either order.
Set operations
Sets also support the maths of collections, which makes them handy for comparing data:
a = {1, 2, 3}
b = {3, 4}
print(a | b) # {1, 2, 3, 4} union
print(a & b) # {3} intersection
print(a - b) # {1, 2} in a but not in b
print(a ^ b) # {1, 2, 4} in exactly one of them
Complexity
- Create an empty set:
O(1). - Build from an iterable of
nitems:O(n). - Add, remove, discard and
in:O(1)on average. In the rare worst case, many values with the same hash, they degrade toO(n). - Loop over all values:
O(n). - Space:
O(n)fornvalues.
Common mistakes
- Using
{}for an empty set. It creates a dictionary. Useset(). - Expecting an order or an index.
fruits[0]raisesTypeError, and the loop order is not part of the contract. - Using
removeon a value that may be missing. It raisesKeyError. Usediscard. - Adding a list.
s.add([1, 2])fails withunhashable type: 'list'. Convert it to a tuple first. - Expecting
True,1and1.0to be different. They are equal and have the same hash, solen({1, 1.0, True})is 1.
Key takeaways
- In Python,
setis the hash set: unique values, fast membership tests. - Create one with
set(),set(iterable)or{...}with values;{}is a dictionary. - Use
add,in,discardand aforloop for the basics. - Elements must be hashable, and the iteration order is not guaranteed.
