Analysis and Solutions for TypeError: unhashable type: 'list' When Removing Duplicates from Lists of Lists in Python

Dec 05, 2025 · Programming · 12 views · 7.8

Keywords: Python | set deduplication | hashability | list processing | TypeError

Abstract: This paper provides an in-depth analysis of the TypeError: unhashable type: 'list' error that occurs when using Python's built-in set function to remove duplicates from lists containing other lists. It explains the core concepts of hashability and mutability, detailing why lists are unhashable while tuples are hashable. Based on the best answer, two main solutions are presented: first, an algorithm that sorts before deduplication to avoid using set; second, converting inner lists to tuples before applying set. The paper also discusses performance implications, practical considerations, and provides detailed code examples with implementation insights.

Error Phenomenon and Problem Description

In Python programming, when attempting to use the built-in set() function to remove duplicates from a list containing other lists as elements, developers often encounter the TypeError: unhashable type: 'list' error. For example, given a list TopP = [[1,2,3,4],[4,5,6,7]], executing sorted(set(TopP), reverse=True) triggers this error. The core issue lies in Python's set requirements for element hashability.

Theoretical Foundation: Hashability and Mutability

Python sets are implemented using hash tables, requiring all elements to be hashable. Hashability means an object has a hash value that remains constant throughout its lifetime, enabling sets to perform efficient membership testing and duplicate removal. Among Python's built-in types, immutable types like strings, numbers, and tuples are hashable, while mutable types like lists and dictionaries are not. This is because if mutable objects could change their hash values, it would break hash table consistency and cause lookup failures.

Specifically, lists are mutable containers whose contents can be modified after creation. If lists were allowed as set elements, when their contents change, their hash values would also change, preventing sets from correctly maintaining element uniqueness. For example:

>>> hash([1, 2, 3])
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    hash([1, 2, 3])
TypeError: unhashable type: 'list'

In contrast, tuples are immutable sequences with fixed hash values after creation:

>>> hash((1, 2, 3))
2528502973977326415

Solution 1: Sort Before Deduplication

Since directly using set() fails due to list unhashability, an effective alternative is to sort the list first, then remove duplicates from the sorted list. This approach avoids hashability requirements while maintaining O(n log n) time complexity, comparable to set-based deduplication.

Complete implementation code:

def uniq(lst):
    """Generator function to remove consecutive duplicates from a sorted list"""
    last = object()  # Use a unique object as initial comparison value
    for item in lst:
        if item == last:
            continue
        yield item
        last = item

def sort_and_deduplicate(l):
    """Sort a list and remove duplicates"""
    return list(uniq(sorted(l, reverse=True)))

This solution works as follows: first, sorted(l, reverse=True) sorts the input list in descending order. Then, the uniq() generator function iterates through the sorted list, skipping items identical to the previous one, thus removing duplicates. The key advantage is complete independence from element hashability, allowing it to handle lists containing other lists.

Usage example:

TopP = [[1, 2, 3], [4, 5, 6], [1, 2, 3], [7, 8, 9]]
result = sort_and_deduplicate(TopP)
print(result)  # Output: [[7, 8, 9], [4, 5, 6], [1, 2, 3]]

Solution 2: Convert to Tuples Before Using Set

Another common approach converts inner lists to tuples, which are immutable and therefore hashable. After conversion, set() can be safely used for deduplication.

Implementation code:

def deduplicate_with_tuples(my_list):
    """Deduplicate by converting to tuples and using set"""
    # Convert each inner list to a tuple
    tuple_list = map(tuple, my_list)
    # Use set for deduplication, then sort
    unique_tuples = sorted(set(tuple_list), reverse=True)
    # Convert results back to list form
    return [list(t) for t in unique_tuples]

More concise one-line implementation:

result = [list(t) for t in sorted(set(map(tuple, my_list)), reverse=True)]

An important detail: if a tuple contains mutable elements (like lists), the tuple remains unhashable. For example:

>>> hash((1, 2, [3, 4]))
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    hash((1, 2, [3, 4]))
TypeError: unhashable type: 'list'

Performance Analysis and Comparison

Both solutions have O(n log n) time complexity, primarily determined by sorting. However, there are practical differences:

  1. Memory Usage: The first solution (sort before deduplication) only needs to store the original and sorted lists, with lower memory overhead. The second solution requires additional storage for tuple copies, using slightly more memory.
  2. Hash Computation Overhead: The second solution needs to compute hash values for each tuple, while the first completely avoids hash computation.
  3. Applicability: If subsequent operations require list-form results, the first solution is more direct. If element immutability needs preservation, the second is more appropriate.

In practice, for small to medium datasets, performance differences are negligible. For large datasets, the first solution may have slight advantages by avoiding hash computations and extra type conversions.

Extended Discussion and Best Practices

Beyond the main solutions, several considerations and best practices are relevant:

  1. Hashability of Custom Objects: For user-defined classes, hashability can be achieved by implementing __hash__() and __eq__() methods. However, if objects are mutable, implementing __hash__() may lead to unpredictable behavior.
  2. Using Frozenset for Nested Structures: For lists containing dictionaries, consider using frozenset instead of tuples, as dictionaries themselves are unhashable.
  3. Error Handling: Practical code should include appropriate error handling, especially when input data may contain unhashable elements.

Enhanced implementation with error handling:

def safe_sort_and_deduplicate(data):
    """Safe sorting and deduplication function with error handling"""
    try:
        # Attempt set-based deduplication
        return sorted(set(data), reverse=True)
    except TypeError as e:
        if "unhashable" in str(e):
            # If unhashable error occurs, use sort-before-deduplication method
            return sort_and_deduplicate(data)
        else:
            # Re-raise other error types
            raise

Conclusion

The TypeError: unhashable type: 'list' error highlights Python's strict requirements for element hashability in sets. When deduplicating lists containing other lists, two main strategies exist: first, an algorithm that sorts before deduplication to avoid hashability requirements; second, converting inner lists to hashable tuples. The first solution is more general and avoids extra type conversions, while the second aligns better with Python idioms. In practice, the choice should be based on specific requirements and context. Understanding these underlying mechanisms not only solves the immediate problem but also deepens comprehension of Python data structures and hashing mechanisms.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.