6.2 Sets
A set stores each distinct value once. A set of allowed team names lets a program check whether a name is recognized. A set of order identifiers lets a program check whether an identifier has already appeared.
Sets are unordered collections. They do not retain element positions or insertion order, so indexing and slicing are unavailable. The printed order can vary. Keep a list when the sequence or number of repeated observations matters.
6.2.1 Creating sets and checking membership
Create a nonempty set by enclosing elements in curly braces {} and separating them with commas. Use set() for an empty set. Empty braces {} create a different collection type, a dictionary, which is introduced later. Table 1 shows sets containing integers, strings, and mixed values.
| Set example | Description |
|---|---|
{1, 2, 3, 4, 5} |
A set of integers. |
{"apple", "banana", "cherry"} |
A set of strings. |
{1, "apple", 2, "banana"} |
A mixed set of integers and strings. |
set() |
An empty set. |
Set elements must be hashable. A hash value is an integer Python uses internally to locate a value. Hashable values have a stable hash value and consistent equality comparisons. Strings and integers meet that requirement; lists and mutable sets do not. A tuple is hashable when all its elements are hashable. The Python set documentation defines these requirements and the absence of positional order.
The membership operators in and not in return True or False. Listing 1 checks team names against a set of allowed names. The spelling and capitalization of each string must match.
The results appear in Listing 2. len() counts distinct elements in the set.
6.2.2 Adding and removing elements
Sets are mutable, so their contents can change. add() adds one element; adding an element already present leaves the set unchanged. remove() removes an element and raises KeyError if it is absent. discard() removes an element if present and otherwise leaves the set unchanged.
Listing 3 separates the value returned by add() from the changed set. add(), remove(), discard(), and clear() return None. Assigning their return value to the variable holding the set would lose that variable's reference to the set.
sorted() returns a sorted list for a predictable display. The set itself still has no positional order. Listing 4 shows the results.
6.2.3 Transforming lists to sets
The set() constructor creates a new set from the elements of a list, retaining each distinct element once. The original list is unchanged. Listing 5 converts a list of numbers to a set.
Conversion is useful for identifying distinct values and applying set operations. A set alone cannot tell you how often each value occurred. Listing 6 keeps both the original observations and the set.
Listing 7 shows that there are four observations and three distinct identifiers. Their counts differ because A02 occurs twice.
6.2.4 Checking allowed values and duplicate identifiers
An allowed-value check tests whether a value belongs to the agreed set. Listing 8 checks a supplied list of team names. The output keeps each observation, including the repeated Assembly name. An unrecognized name is reported for review; the original list remains available.
The results appear in Listing 9.
An identifier is a value used to distinguish one item from another. If each order should appear once, a repeated order identifier needs investigation. The set seen_ids in Listing 10 records the identifiers encountered so far. Check membership before adding the current identifier.
The first A01 and A02 are added to the set when first encountered. Their later occurrences are already present and are reported in Listing 11. The membership check reports a repeated identifier; deciding how to correct the source data requires the meaning of the observations. Keep the original observations until that decision is made.
6.2.5 Finding common values
An intersection contains the values present in both sets. For example, two teams may have lists of order identifiers to review. Their intersection identifies the orders appearing on both lists. The intersection() method returns a new set and leaves both input sets unchanged. Listing 12 finds the common identifiers.
Listing 13 shows the common identifiers in sorted order for display. Repeated observations remain in the source lists.
A required output order needs a separate rule. To return common identifiers in their first-occurrence order on the Assembly list, scan that list from left to right. Use the Packing set to check membership and a second set to record identifiers already added to the result. Append each qualifying identifier once to a result list. For these observations, that rule gives ['A03', 'A02']. Iterating over the intersection set would not preserve that order.
6.2.6 Sorting distinct values
sorted() can turn a set into a list in ascending order. For numbers, ascending order places smaller values before larger ones. Converting the observations to a set removes repetitions; sorting specifies the output order. Both operations create new collections.
Some tasks also restrict the values to include. In Listing 14, each row contains observations, and only integers from 1 through 10, inclusive, qualify. type(value) is int checks for an integer exactly. Boolean values such as True have type bool and are excluded by that check. The nested loops visit every value in every row before the distinct qualifying values are sorted.
Listing 15 shows that the repeated 4 appears once, the out-of-range values are excluded, and the rows remain unchanged.
6.2.7 Further study
Set methods and operations
Sets provide methods for adding and removing elements, and for set operations such as union, intersection, and difference. Table 2 lists the methods for s = {1, 2, 3, 4, 5}. Each row starts with a fresh set, so the rows are independent. A displayed set lists its elements without promising their printed order.
s = {1, 2, 3, 4, 5}; set display order is unspecified.
| Set method | Description | Example | Return value or resulting elements |
|---|---|---|---|
set.add(element) |
Adds an element to the set. | s.add(6) |
Returns None; s becomes {1, 2, 3, 4, 5, 6}. |
set.remove(element) |
Removes an element. Raises KeyError if absent. |
s.remove(3) |
Returns None; s becomes {1, 2, 4, 5}. |
set.discard(element) |
Removes an element if present. An absent element causes no error. | s.discard(3) |
Returns None; s becomes {1, 2, 4, 5}. |
set.pop() |
Removes and returns an arbitrary element. Raises KeyError if empty. |
s.pop() |
Returns any one element, such as 1; s loses that element. |
set.clear() |
Removes all elements. | s.clear() |
Returns None; s becomes set(). |
len(set) |
Returns the number of elements in the set. | len(s) |
5 |
element in set |
Returns True if the element is present in the set, False otherwise. |
2 in s |
True |
set.union(other) |
Returns a new set with elements from both sets. | s.union({4, 5, 6}) |
{1, 2, 3, 4, 5, 6} |
set.intersection(other) |
Returns a new set with elements common to both sets. | s.intersection({3, 4, 5, 6}) |
{3, 4, 5} |
set.difference(other) |
Returns a new set with elements in the set but not in the other set. | s.difference({3, 4}) |
{1, 2, 5} |
set.symmetric_difference(other) |
Returns a new set with elements in either set but not in both sets. | s.symmetric_difference({4, 5, 6}) |
{1, 2, 3, 6} |
set.issubset(other) |
Returns True if the set is a subset of the other set. |
s.issubset({1, 2, 3, 4, 5, 6}) |
True |
set.issuperset(other) |
Returns True if the set is a superset of the other set. |
s.issuperset({1, 2, 3}) |
True |
A union contains all elements from either set. An intersection contains elements common to both sets. A difference contains elements in the first set that are absent from the second. A symmetric difference contains elements that appear in exactly one set. These methods return a new set and leave s unchanged.
A set is a subset of another when every element of the first occurs in the second. It is a superset when it contains every element of the second. issubset() and issuperset() return Boolean values. pop() selects an arbitrary element; it has no promise of selecting the smallest, first inserted, or random element.
Set comprehension
Set comprehension creates a new set by applying an expression to each element of an iterable, such as a list or set. An optional condition selects the elements to process. Repeated resulting values appear once. Table 3 shows the expression and conditional forms.
| Set comprehension | Description | Example | Resulting elements |
|---|---|---|---|
{expression for element in iterable} |
Applies the expression to each element in the iterable. | {x**2 for x in {1, 2, 3, 4, 5}} |
{1, 4, 9, 16, 25} |
{expression for element in iterable if condition} |
Applies the expression to each element that satisfies the condition. | {x**2 for x in {1, 2, 3, 4, 5} if x % 2 == 0} |
{4, 16} |
6.2.8 Exercises
The following design exercises give optional practice in collecting distinct values and finding ordered common values.
6.2.Q1 Collect sorted distinct integer values
A table of observations is stored as a list of rows. Each row is a list containing integers or Boolean values. Design an algorithm that uses a set to collect every distinct qualifying integer, then returns those integers as a list in ascending order.
Qualifying integers are from 1 through 10, inclusive. Exclude values outside that range and exclude Boolean values. Leave every source row unchanged. Return [] if the table is empty or no values qualify. Rows may have different lengths, including zero.
The input in Listing 16 must produce [1, 3, 6, 10].
Present your algorithm in Python, pseudocode, or a flowchart. If you use Python, name the function sorted_distinct_values(data). You may use sorted(). Give checks with inputs and expected results for repetitions, range boundaries, Boolean values, and empty input.
6.2.Q2 Find common values in their original order
Two measurement lists can contain repeated numbers. Design an algorithm that receives first and second and returns a new list of the numbers present in both inputs. Use sets to check membership.
- Each common number must appear once, in the order of its first occurrence in
first. - Both inputs contain finite integers or decimal numbers. Compare numbers by their value;
1and1.0count as the same number. - Leave both input lists unchanged. Return
[]when either list is empty or when the lists have no common numbers.
For the inputs in Listing 17, the required result is [8, 3, 1]. The order of the second list does not determine the output order.
- Present your algorithm in pseudocode, a flowchart, or Python. If you use Python, name the function
ordered_common_values(first, second). - Give checks with inputs and expected results for repeated numbers, empty input, and lists with no common numbers.
Use membership checks to report duplicate order identifiers in the next exercise.
6.2.Q3 Report duplicate identifiers
An order log contains the identifiers in Listing 18. Each identifier should describe one order, so repeated identifiers need review. Retain the original list and its observations.
Write a Python function find_duplicate_ids(order_ids) that uses sets to check identifiers and returns a list of repeated identifiers. Include each repeated identifier once, in the order in which its second occurrence is encountered. For the supplied input, return ['A02', 'A01'].
Assume every identifier is a nonempty string. Compare the strings exactly. The function must leave the input list unchanged and return an empty list when no identifier repeats. Check your function with cases that cover empty input, no repeats, and one identifier appearing more than twice.
For optional tracing practice, compare the presence-array approach below with the set-based approach you used for sorted distinct values.
6.2.M1 Nested-list processing
In Listing 19, the Python function x receives a non-empty two-dimensional list of integers and returns a list.
Considering the two-dimensional list in Table 4, what is returned when x is called with that list?
| A | B | C | D | |
|---|---|---|---|---|
| 1 | 7 | 2 | 11 | 10 |
| 2 | 11 | 1 | 2 | 1 |
| 3 | 9 | 7 | 0 | -1 |
What is the returned list?
[1, 2, 7, 9, 10][7, 2, 11, 10, 1, 2, 9][2, 0, 0, 0, 0, 0, 2, 0, 1, 1][7, 2, 11, -1, 10, 1, 2, 9, 7, 0, -1]
range(start, stop, step)supplies integers fromstarttoward, but excluding,stop.stepdefaults to1, andrange(stop)starts at0.list(range(2, 7, 2))is[2, 4, 6];list(range(5, 1, -2))is[5, 3].- A range is empty when the step cannot reach the stop; a zero step raises
ValueError.
listcollects the supplied values in a new list.items.append(value)adds one item at the end of the list and returnsNone. Starting withitems = [8],items.append(3)changes it to[8, 3]. An appended list remains one nested item; an empty list accepts its first item the same way.