# 127. Custom Sorting Functions

In Python, custom sorting of complex data structures can be achieved by using the `sorted()` function or the `sort()` method with a custom comparison function. Python’s sorting mechanism is based on the `key` parameter, which is a function that transforms the items before sorting. Here's a series of examples that demonstrate how to use custom sorting functions to sort complex data structures:

**1. Sorting List of Tuples by Second Element**

```python
data = [(1, 'apple'), (3, 'orange'), (2, 'banana')]

# Sorting by the second element of each tuple
sorted_data = sorted(data, key=lambda x: x[1])
print(sorted_data)  # Output: [(1, 'apple'), (2, 'banana'), (3, 'orange')]
```

***

**2. Sorting a List of Dictionaries by Multiple Keys**

```python
data = [
    {'name': 'John', 'age': 25, 'city': 'New York'},
    {'name': 'Jane', 'age': 22, 'city': 'Los Angeles'},
    {'name': 'Alice', 'age': 30, 'city': 'Chicago'}
]

# Sorting by age, then by name
sorted_data = sorted(data, key=lambda x: (x['age'], x['name']))
print(sorted_data)
# Output: [{'name': 'Jane', 'age': 22, 'city': 'Los Angeles'}, {'name': 'John', 'age': 25, 'city': 'New York'}, {'name': 'Alice', 'age': 30, 'city': 'Chicago'}]
```

***

**3. Sorting Objects by Multiple Attributes**

```python
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"Person(name={self.name}, age={self.age})"

people = [Person('John', 25), Person('Alice', 30), Person('Bob', 25)]

# Sorting by age, then by name
sorted_people = sorted(people, key=lambda x: (x.age, x.name))
print(sorted_people)
# Output: [Person(name=Bob, age=25), Person(name=John, age=25), Person(name=Alice, age=30)]
```

***

**4. Sorting Strings by Length**

```python
words = ['apple', 'banana', 'pear', 'cherry', 'kiwi']

# Sorting by length of the string
sorted_words = sorted(words, key=len)
print(sorted_words)  # Output: ['kiwi', 'pear', 'apple', 'banana', 'cherry']
```

***

**5. Sorting List of Tuples in Descending Order**

```python
data = [(1, 'apple'), (3, 'orange'), (2, 'banana')]

# Sorting by the first element in descending order
sorted_data = sorted(data, key=lambda x: x[0], reverse=True)
print(sorted_data)  # Output: [(3, 'orange'), (2, 'banana'), (1, 'apple')]
```

***

**6. Sorting with a Custom Comparison Function Using `cmp_to_key`**

In Python 3, `cmp` functions have been removed. However, you can still use them with `functools.cmp_to_key`.

```python
from functools import cmp_to_key

def compare_tuples(a, b):
    return a[0] - b[0]  # Compare by the first element

data = [(1, 'apple'), (3, 'orange'), (2, 'banana')]

sorted_data = sorted(data, key=cmp_to_key(compare_tuples))
print(sorted_data)  # Output: [(1, 'apple'), (2, 'banana'), (3, 'orange')]
```

***

**7. Sorting List of Dictionaries by String Length**

```python
data = [
    {'name': 'John', 'age': 25},
    {'name': 'Jane', 'age': 22},
    {'name': 'Alexander', 'age': 30}
]

# Sorting by length of the name field
sorted_data = sorted(data, key=lambda x: len(x['name']))
print(sorted_data)  
# Output: [{'name': 'Jane', 'age': 22}, {'name': 'John', 'age': 25}, {'name': 'Alexander', 'age': 30}]
```

***

**8. Sorting Dates in a List of Tuples**

```python
from datetime import datetime

dates = [
    ('2024-01-01', 'New Year'),
    ('2023-12-25', 'Christmas'),
    ('2024-07-04', 'Independence Day')
]

# Convert string to datetime and sort
sorted_dates = sorted(dates, key=lambda x: datetime.strptime(x[0], '%Y-%m-%d'))
print(sorted_dates)  
# Output: [('2023-12-25', 'Christmas'), ('2024-01-01', 'New Year'), ('2024-07-04', 'Independence Day')]
```

***

**9. Sorting List of Mixed Types (Integers and Strings)**

```python
data = [3, 'banana', 1, 'apple', 2, 'cherry']

# Sorting by type first, then by value
sorted_data = sorted(data, key=lambda x: (isinstance(x, str), x))
print(sorted_data)  
# Output: [1, 2, 3, 'apple', 'banana', 'cherry']
```

***

**10. Custom Sorting with Tuple and Custom Comparator**

```python
data = [(5, "task5"), (2, "task2"), (3, "task3"), (1, "task1")]

# Custom comparator function to sort by the second element in reverse order
def custom_comparator(x):
    return -len(x[1])

# Sorting using the custom comparator
sorted_data = sorted(data, key=custom_comparator)
print(sorted_data)  
# Output: [(5, 'task5'), (3, 'task3'), (2, 'task2'), (1, 'task1')]
```

***

These examples show how to sort complex data structures using custom sorting logic, such as by multiple keys, object attributes, string lengths, or through custom comparison functions. By leveraging Python's `sorted()` function and `key` parameter, you can easily sort data based on a variety of criteria without writing complex sorting algorithms from scratch.
