# Tuples and Sorting¶

Today, we will discuss the following:

• A new immutable sequence: tuples

• Sorting with `reverse` and a `key` function

## New Immutable Sequence: Tuples¶

• Tuples are an immutable sequence of values separated by commas and enclosed within parentheses ( )

• Tuples support any sequence operations that don’t involve mutation: e.g., `len()`, indexing, slicing, concatenation, etc

• Tuples support simple and nifty assignment syntax, which makes them really convenient

```# string tuple
names = ('Iris', 'Jeannie', 'Lida')

# num tuple
primes = (2, 3, 5, 7, 11)

# singleton
num = (5,)

# parens are optional
values = 5, 6

# empty tuple
emp = ()
```
```type(values)
```
```tuple
```

### Tuples as a Sequence¶

Like strings and lists, we can use sequence operations and functions to manipulate tuples. See examples below.

```nameTuple = ('Iris', 'Jeannie', 'Lida')
```
```len(nameTuple)
```
```3
```
```nameTuple
```
```'Lida'
```
```# Can't do this because tuples are immutable!
nameTuple = 'Kelsey'
```
```---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In , in <cell line: 2>()
1 # Can't do this because tuples are immutable!
----> 2 nameTuple = 'Kelsey'

TypeError: 'tuple' object does not support item assignment
```
```# concatenation returns a new sequence
nameTuple + ('Lida', )
```
```('Iris', 'Jeannie', 'Lida', 'Lida')
```
```# what will this do?
nameTuple * 2
```
```('Iris', 'Jeannie', 'Lida', 'Iris', 'Jeannie', 'Lida')
```
```numTuple = (1, 1, 2, 3, 5, 8, 13)
```
```# slicing returns a new tuple
numTuple[3:6]
```
```(3, 5, 8)
```
```numTuple[::-1]
```
```(13, 8, 5, 3, 2, 1, 1)
```
```colors = ('red', 'blue', 'orange', 'white', 'black')
```
```# can use in and not in for testing membership
'green' not in colors
```
```True
```
```'Red' in colors
```
```False
```
```# can iterate over tuples using for loop like any other sequence
for c in colors:
print(c)
```
```red
blue
orange
white
black
```

## Multiple Assignments and Sequence Unpacking with Tuples¶

Tuples are very useful for:

• assigning multiple values in a single line

• simple sequence “unpacking”

• returning multiple values from functions

```a, b = 4, 5
```
```a
```
```4
```
```b
```
```5
```
```# easily swap values
b, a = a, b
```
```a, b
```
```(5, 4)
```
```a, b, c = (1, 2, 3)
```
```print(a, b, c)
```
```1 2 3
```
```# will this work? (no, too many values)
a, b = 1, 2, 3
```
```---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In , in <cell line: 2>()
1 # will this work? (no, too many values)
----> 2 a, b = 1, 2, 3

ValueError: too many values to unpack (expected 2)
```
```studentInfo = ['Charlie Brown', 8, 'cookie dough']
```
```#name = studentInfo
#age = studentInfo
#house = studentInfo

# nifty short hand for three separate assignments (shown above) using a tuple
name, age, icecream = studentInfo
```
```print(name, age, icecream)
```
```Charlie Brown 8 cookie dough
```
```name
```
```'Charlie Brown'
```
```# multiple return values as a tuple
def arithmetic(num1, num2):
'''Takes two numbers and returns the sum and product'''
return num1 + num2, num1 * num2
```
```arithmetic(10, 2)
```
```(12, 20)
```
```type(arithmetic(3, 4))
```
```tuple
```

## Conversion between Sequences¶

We can convert between `str`, `list`, `range`, and `tuple` types by using the corresponding functions.

```word = "Williamstown"
```
```# create list of characters
charList = list(word)
```
```charList
```
```['W', 'i', 'l', 'l', 'i', 'a', 'm', 's', 't', 'o', 'w', 'n']
```
```# create tuple of characters
charTuple = tuple(charList)
```
```charTuple
```
```('W', 'i', 'l', 'l', 'i', 'a', 'm', 's', 't', 'o', 'w', 'n')
```
```# tuple to list
list((1, 2, 3, 4, 5))
```
```[1, 2, 3, 4, 5]
```
```numRange = range(12)
```
```# range to list
list(numRange)
```
```[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
```
```# list to string
str(list(numRange))
```
```'[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]'
```
```# tuple to string
str(('hello', 'world'))
```
```"('hello', 'world')"
```

## Sorting Sequences¶

We have talked about the special `sort()` method for lists that takes advantage of list mutability to sort lists in place. sort() is a methond and only works for lists!

The `sorted()` function that takes a sequence and returns a new sorted sequence as a `list`.

By default, `sorted()` sorts the sequence in ascending order (for numbers) and alphabetical (dictionary) order for strings.

Furthermore, strings are sorted based on the ASCII value of their characters: special characters come before capital letters, which come before lower-case letters.

IMPORTANT `sorted()` does not alter the sequence it is called on. It returns always returns a new `list`.

```# calling sorted() on a tuple returns a sorted list
nums = (42, -20, 13, 10, 0, 11, 18)
sorted(nums)
```
```[-20, 0, 10, 11, 13, 18, 42]
```
```letters = ('a', 'z', 'e', 'p', 'c')
sorted(letters)
```
```['a', 'c', 'e', 'p', 'z']
```
```# sorted() will sort the characters in the string and return a list
sorted("Iris")
```
```['I', 'i', 'r', 's']
```
```sorted("Jeannie")
```
```['J', 'a', 'e', 'e', 'i', 'n', 'n']
```
```sorted("*hello! world!*")
```
```[' ', '!', '!', '*', '*', 'd', 'e', 'h', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
```
```# this will sort the string, create a new list, and then turn the list into a string
''.join(sorted("*hello! world!*"))
```
```' !!**dehllloorw'
```

### ASCII values of characters¶

Strings are sorted based on the ASCII values of their characters. We can investigate these values using `ord()` and `chr()`.

```# ord() returns the ASCII value (int) of a string
ord('a')
```
```97
```
```ord('A')
```
```65
```
```ord('!')
```
```33
```
```# chr returns the string for a given ASCII value (int)
chr(33)
```
```'!'
```
```chr(65)
```
```'A'
```
```chr(97)
```
```'a'
```

## Sorting Tuples and More¶

Recall the `sorted()` function that takes a sequence and returns a new sorted sequence as a `list`.

By default, `sorted()` function on a sequence containing tuples has the following behavior:

• Sorts tuples by first item of each tuple

• If there is a tie (e.g., two tuples have the same first item), it sorts them by comparing their second item, so on.

This sorting behavior is pretty standard and is referred to as lexigraphical sorting.

Note: `sorted()` also behaves this way with lists and lists of lists.

```fruits = [('12', 'apples'), ('kiwis', '5'), ('4', 'bananas'), ('27', 'grapes')]
sorted(fruits)
```
```[('12', 'apples'), ('27', 'grapes'), ('4', 'bananas'), ('kiwis', '5')]
```
```pairs = [('4', '5'), ('0', '2'), ('12', '1'), ('11', '3')]
sorted(pairs)
```
```[('0', '2'), ('11', '3'), ('12', '1'), ('4', '5')]
```
```pairs
```
```[('4', '5'), ('0', '2'), ('12', '1'), ('11', '3')]
```
```triples = [(1, 2, 3), (1, 3, 2), (2, 2, 1), (1, 2, 1)]
sorted(triples)
```
```[(1, 2, 1), (1, 2, 3), (1, 3, 2), (2, 2, 1)]
```
```characters = [(8, 'a', '\$'), (7, 'c', '@'),
(7, 'b', '+'), (8, 'a', '!')]

sorted(characters)
```
```[(7, 'b', '+'), (7, 'c', '@'), (8, 'a', '!'), (8, 'a', '\$')]
```
```sorted((4,5,1,2))
```
```[1, 2, 4, 5]
```

## Changing the Default Sorting Behavior¶

Sometimes we may want to sort based on the second item in a tuple, or perhaps in descending order. We can tell Python how to sort and override the default sequence sorting behavior. To do so, let us explore the `sorted()` function and its optional arguments.

```help(sorted)
```
```Help on built-in function sorted in module builtins:

sorted(iterable, /, *, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.

A custom key function can be supplied to customize the sort order, and the
reverse flag can be set to request the result in descending order.
```

### Sorting using `reverse`¶

```sorted([8, 2, 3, 1, 3, 1, 2], reverse=True)
```
```[8, 3, 3, 2, 2, 1, 1]
```
```sorted(['a', 'c', 'e', 'p', 'z'], reverse=True)
```
```['z', 'p', 'e', 'c', 'a']
```
```fruits = [(12, 'apples'), (5, 'kiwis'), (4, 'bananas'), (27, 'grapes')]
sorted(fruits, reverse=True)
```
```[(27, 'grapes'), (12, 'apples'), (5, 'kiwis'), (4, 'bananas')]
```

### Sorting using `key` function¶

Now suppose we have a list of tuples that we want to sort by something other than the first item.

For example:

• Consider a list of tuples, where the first item is a course name, second item is the cap, and third item is the term.

• Say we want to sort these courses by their capacity: courses with higher capacity should come first.

We can accomplish this by supplying the `sorted()` function with a `key` function that tells it how to compare the tuples to each other.

```courses = [('CS134', 90, 'Spring'), ('CS136', 60, 'Spring'),
('AFR206', 30, 'Spring'), ('ECON233', 30, 'Fall'),
('MUS112', 10, 'Fall'), ('STAT200', 50, 'Spring'),
('PSYC201', 50, 'Fall'), ('MATH110', 90, 'Spring')]
```
```# by default, Python sorts by first element in tuples
sorted(courses)
```
```[('AFR206', 30, 'Spring'),
('CS134', 90, 'Spring'),
('CS136', 60, 'Spring'),
('ECON233', 30, 'Fall'),
('MATH110', 90, 'Spring'),
('MUS112', 10, 'Fall'),
('PSYC201', 50, 'Fall'),
('STAT200', 50, 'Spring')]
```
```def capacity(courseTuple):
'''Takes a sequence and returns item at index 1'''
return courseTuple
```
```# we can tell sorted() to sort by capacity instead
sorted(courses, key=capacity)
```
```[('MUS112', 10, 'Fall'),
('AFR206', 30, 'Spring'),
('ECON233', 30, 'Fall'),
('STAT200', 50, 'Spring'),
('PSYC201', 50, 'Fall'),
('CS136', 60, 'Spring'),
('CS134', 90, 'Spring'),
('MATH110', 90, 'Spring')]
```
```# we can also combine the key and reverse parameters
# to sort by capacity in descending order
sorted(courses, key=capacity, reverse=True)
```
```[('CS134', 90, 'Spring'),
('MATH110', 90, 'Spring'),
('CS136', 60, 'Spring'),
('STAT200', 50, 'Spring'),
('PSYC201', 50, 'Fall'),
('AFR206', 30, 'Spring'),
('ECON233', 30, 'Fall'),
('MUS112', 10, 'Fall')]
```