---
title: Linux & scripting for data wrangling
author: Jimmy Christensen
theme: Linux, Bash, Python
date: October 31, 2022
duration: 120
---

# Overview

- Linux
- Bash
- Python

Slides available at https://dusted.dk/pages/scripting/slides.html

# Linux ?
Linux is an operating system, like Windows or OSX, it allows you to run programs.

# Bash ?
Popular "command line" for Linux & OSX

- Launch programs
- Interact with files and folders
- Composition via pipes
- Scripting language
- Easy things, very easy
- Medium things, hard
- Hard things, very hard

# Python ?
Popular scripting language

- Easy to learn
- General purpose
- Easy things are easy enough
- Medium things are not so bad
- Hard things are less hard

# All of them ?

Bash, grep, awk, sed, tr, cut:

- Very fast
- Easy things are easy
- Everything else is really hard
- Use for rough extraction

Bash scripts using above programs to automate extraction.

Python scripts to remodel and consolidate extracted data, calculate and prepare results.


# Overview: Linux
- Editing with nano
- Files / Directories
- Pipes & redirects


# Editing with nano
Nano is a (bit too) simple text editor

- nano myfile.sh
- ctrl+o  to op... save!
- ctrl+w  to wri.. search!
- ctrl+k  to k.... cut!
- ctrl+u  to yes.. insert! ;-)
- ctrl+x  to eXit

# Files / Directories

/home/chrisji/theWork/important.txt

- pwd
- cd
- ls -lah
- hd, head, tail, more, du, wc

Peek at unknown file: hd strangeFile | head

Check size: ls -lh strangeFile

Only beginning: head strangeFile

Page through it: more strangeFile

# Pipes & redirects

- Programs will read data from standard input (stdin)
- Programs will write data to standard output (stdout)
- Pipe directs stdout into the next programs stdin
- programA | programB | programC
- \> and >> directs stdout to a file

# Pipes & redirects
Composition, also useful in bash scripting
```bash
# A pipe directs data from stdout of one program to stdin of the next
ls | wc -l

# A redirect directs data from stdout of a program to a file.
ls > myFiles.txt

# An appending redirect will not truncate the file first.
echo "Line 1" > file.txt
echo "Line 2" >> file.txt
```

# Script vs program
Same difference, almost

- Scripts typically easier to write for the human
- Scripts typically slower to execute for the computer
- Scripts are "clear text"


# Overview: Bash

- Making & running a bash script
- Variables & parameters
- Conditionals & loops
- Reading data
- Functions
- Debugging
- Tips

# Two quick notes

```bash
# This line is a comment, because it starts with the hash mark
# comments are to help the humans (us)

both bash and python # ignores a hash mark and any following text
but only for rest of that line
```
- Scripts are executed by the computer one line at a time, not "all at once".

# Making & running a bash script

Creating the script with the nano editor
```bash
nano shee.sh
```
![](./bash1.png)

- ctrl +o to save the file
- ctrl +x to exit nano

# Making & running a bash script


```bash
# Look at our new script file, it is not marked as being "executable"
ls -l shee.sh

# We make it executable!
chmod +x shee.sh

# Check that it has the x flag (and pretty green color)
ls -l shee.sh

# Now we can run it
/shee.sh
Hello World!
```
![](./bash2.png)

# Bash: Variables

![](var.png)

Holds something, has a name, like box with a label on it

```bash
#!/bin/bash

placeToGreet="World"

echo "Hello $placeToGreet!"
```

```
Hello World!
```

# Bash: Variables

![](var.png)

They can be re-assigned!

```bash
#!/bin/bash

placeToGreet="World"

placeToGreet="Earth"

echo "Hello $placeToGreet!"
```
What's the result ? (remember, line-by-line)

# Bash: Variables

A bash variable can store about 2 megabytes of data, python about 64 gigabytes

```bash
#!/bin/bash

# Create some variables and assign them values
timesToCall=3
animal="dog"
name="viggo"

# Output to the screen
echo "Calling for $name the $animal $timesToCall times!"
```
```
Calling for viggo the dog 3 times!
```

# Bash: Parameters
Variables assigned from command-line

```bash
# The shee.sh script is executed with four parameters:
# First parameter: 3
# Second parameter: dog
# Third parameter: viggo
# Fourth parameter: heere doggie doggie!
/shee.sh 3 dog viggo "heere doggie doggie!"
Calling for viggo the dog 3 times!
```

# Bash: Parameters
```bash
#!/bin/bash

# $1 = first parameter
# $2 = second parameter

timesToCall=$1
animal="$2"
name="$3"

echo "Calling for $name the $animal $timesToCall times!"
```

# Variables: There's more

Bash has strong string-manipulation capabilities, quickly degenerates into alphabet soup

Don't go there, use python

For the curious: https://tldp.org/LDP/abs/html/string-manipulation.html

It also has arrays and sets, bash syntax makes it painful

# Conditionals

Conditionals let's us decide if we want to do something

```bash
if CONDITION
then
   # do
   # some
   # things
fi
```

# Conditionals

The "else" word lets us do something if the condition is not met

```bash
if CONDITION
then
   # do
   # some
   # things
else
   # do some
   # other
   # things
fi
```

# Conditionals

The elif is optional, allows chains

```bash
if CONDITION_A
then
   # something to do if A..
elif CONDITION_B
then
   # This must not happen if A, but if B.
elif CONDITION_C
then
   # If neither A nor B, but C, then do this
else
   # If not A, B nor C, then do this..
fi
```
else is also optional
```bash
if CONDITION_A
then
   # Do this if A
elif CONDITION_B
then
   # Do this if B
fi

```

# Conditionals

In bash, a condition is met if the [ ] builtin or a program exit with the "success" status code.

```bash
true # A program that returns success
false # A program that returns.. not success
grep # Will return success if it finds at least one match
[ "$name" == "viggo" ] # Will success if the variable contains the text viggo
[ $a -gt $b ] # success if a is Greather Than b
[ $a -lt $b ] # success if a is Less Then b
[ $a -eq $b ] # success if a is EQual to b
[ -z ${a+x} ] # success if a is empty (weird syntax, I know)
[ "$name" == "viggo" ] && [ "$animal" == "dog" ] # success only when name is viggo AND animal is dog (both must be true)
[ "$name" == "viggo" ] || [ "$animal" == "dog" ] # success if name is viggo or animal is dog (one or both must be true)
```

# Conditionals

```bash
if echo "secret string" | grep "secret" &> /dev/null
then
   echo "The secret string was found!"
fi
```

# Conditionals

```bash
if [ "$name" == "viggo" ] && [ "$animal" == "dog" ]
then
   echo "Viggo is a very special dog!"
elif [ "$animal" == "dog" ]
then
   echo "Dogs are just great, even if they're $name instead of viggo!"
else
   echo "I'm sure $name is a great $animal!"
fi
```

# Loops

- for loop
- while loop
- do while loop (rarely used)

# Loop: for

Repeats for each item in sequence

```bash
for VARIABLE in SEQUENCE
do
   # things to do
done
```

# Loop: for

Example

```bash
for number in 1 2 3 4
do
   echo "Number is $number"
done
```

# Loop: for

```bash
animals="dog fish cat bird"

for animal in $animals
do
   echo "$animal is an animal"
done
```
```
dog is an animal
fish is an animal
cat is an animal
bird is an animal
```

# Loop: for

```bash
animals="dog fish cat bird"

for animal in "$animals"
do
   echo "$animal is an animal"
done
```
```
dog fish cat bird is an animal
```

# Loop: for

Use expansion for files, this produces a sequence of .txt files in current directory

```bash
for file in *.txt
do
   echo "Text file: $file"
done
```

# Loop: while

Repeats as long as CONDITION is met (potentially forever)

```bash
while CONDITION
do
   # thing to do
done
```

# Interjection

This is a good time to remind you that pressing ctrl+c (eventually) terminates a running script.

# Loop: while

When you need to repeat something until "things are done"

```bash
while CONDITION
do
   # Thing to do until CONDITION is no longer met
done
```

# Loop: while

```bash
while read line
do
   echo "This just in: $line"
done
```

# Reading data

- From human
- From file
- From program
- From stream

# Reading data: The read builtin

```bash
read VARIABLE # Creates a variable and reads data from standard input into it
```

# Read data from human

Useful for interactive scripts

```bash
echo "Welcome to super cool script v 24!"

# The -p parameter is a prompt to show to the human
read -p "File to corrupt: " fileName

echo "Don't worry, I was just kidding, nothing happened to $fileName"
```
```
Welcome to super cool script v 24!
File to corrupt: homework.md
Don't worry, I was just kidding, nothing happened to homework.md
```

# Read data from human (in a loop)

```bash

answer=""
while [ "$answer" != "y" ] && [ "$answer" != "n" ]
do
   read -p "Continue? [y/n] ?" answer
done

if [ "$answer" == "n" ]
then
   echo "Stopping here."
   exit
fi

echo "Continuing"
```
```
Continue? [y/n] ?yes
Continue? [y/n] ?...
Continue? [y/n] ?yeees
Continue? [y/n] ?noooo ?
Continue? [y/n] ?n
Stopping here.
```
# Read data from file

For small files, we can read the entire file into a variable by running the cat program and capturing its output in a variable

```bash
someFileContents=$(cat secrets.txt)

echo "File content: $someFileContents"
```

# Read data from program

For small outputs, we can capture the entire standard (non-error) output into a variable

```bash
gloriousResult=$(grep 'someRegex' secrets.txt)
```

# Read data from stream

Read exits with success unless standard input is closed

```bash
grep 'someRegex' secrets.txt | while read line
do
   echo "A line of the result: $line"
done
```

# Read data from stream

We can compose complex pipelines now

```bash
grep 'someRegex' secrets.txt | while read line
do
   # Extract some field from the line
   importantThing=$(echo $line | cut -f 2 -d ',' | awk magic things)
   # Look for that field in some other file, put the result in a variable
   if resultFromOtherFile=$(grep $importantThing otherSecrets.txt)
   then
       # Eureka! some correlation is interesting when the field from subset of secrets.txt is in otherSecrets.txt!
       # Maybe combine on one line and print it out?
       echo "$line,$resultFromOtherFile"
   fi
done > results.txt
```

# Read data from stream

We can also write a script that takes a stream from a pipe, for use with other scripts

ourCoolScript.sh

```bash
#!/bin/bash

while read ourLine
do
   # Do intersting things with ourLine
done
```

We'd use it like this
```bash
grep 'someRegex' someFile.txt | ./ourCoolScript.sh > results.txt
```

# Write data

We just use the > and >> after any command, or after the script itself.

```bash
> # Will create an empty file (or truncate an existing one)
>> # Will create an empty file (or append to an existing one)
```
# Functions

- A small "script inside your script"
- Can take parameters
- Useful for repetitive tasks (don't repeat yourself)

# Functions

```bash
function removeRepeatSpaces {
   tr -s " "
}

echo "Badly formatted    data right  here." | removeRepeatSpaces
```

```bash
function largestOne {
   if [ $1 -gt $2 ]
   then
       echo $1
   elif $1 -lt $2 ]
   then
       echo $2
   fi
}

largest=$(largestOne $A $B)
```

# Debugging
See _EVERYTHING_ that happens during script execution by adding set -x

```bash
#!/bin/bash
set -x
echo "My script"
read -p "What is your name ?" name

if [ "$name" == "viggo" ]
then
   echo "Good dog!"
fi
```
![](./bash3.png)

# Tips

- Comment
- Indent
- Use good variable names
- Filter before iterating
- Do as much filtering as is convenient, but not more
- Google

# Tips

- If script takes parameters, have it write how to use it
```bash
#!/bin/bash

# This script needs 3 paramters, name, animal and number of legs

name=$1
animal=$2
nlegs=$3

if [ -z ${nlegs+x} ]
then
   echo "Usage $0 NAME ANIMAL NUM_LEGS"
   echo "    NAME - The name of the pet (example: viggo)"
   echo "    ANIMAL - What kind of animal is it? (example: dog)"
   echo "    NUM_LEGS - How many legs does it have? (example: 4)"
   exit 1
fi
```
Even if you're the only user, you might forgot how to use it later

$0 is special, it is always the name of the script in question.

# Tips

Suggestion for treating multiple files while keeping track of the source of each result

Let there be binary files data1.bin data2.bin ... dataN.bin that can be translated
by a "translatebinarytotext" program and filtered by some regular expression to find
relevant lines:

```bash
#!/bin/bash

for fileName in data*.bin
do
   translatebinarytotext "$fileName" \
       | grep 'someRegex' \
       | ./ourAwesomeScript.sh "$fileName" \
       >> result.txt
done
```
```bash
# Alternative if the files are unorderly
for fileName in data1.bin data_2.bin thirdPartOfData.bin
```
The polished version of above alternative could be used like a real program.

```bash
/processThePeskyFiles.sh data1.bin data_2.bin thirdPartOfData.bin > result.txt
```
Then the script would look like:
```bash
#!/bin/bash

for fileName in $@
do
   translatebinarytotext "$fileName" \
       | grep 'someRegex' \
       | ./ourAwesomeScript.sh "$fileName"
done
```

# Tips

It can be useful to write out such "debug" information to the standard error channel, this way it goes "around" pipes and are shown on screen.

```bash

#!/bin/bash

# This function writes a message to stderr, so our script output can be piped while we still see information/errors.
function msg {
   echo $@ >&2
}

msg "Script for doing cool stuff running in $(pwd)"
echo "Important data output"
```
```
bash coolStuffDoer.sh > test.txt
Script for doing cool stuff running in /home/chrisji/theWork/
cat test.txt
Important data output
```

# Tips

Script and data does not need to be the same place, you can write the script as if it is next to the files.

```text

├── data
│   └── seta
│       └── wool.txt
└── scripts
   └── sheep.sh

```

scripts/sheep.sh:
```bash
#!/bin/bash

sheep=$(grep sheep wool.txt | wc -l)
echo "There are $sheep sheep."
```
```bash
cd data/seta
./../scripts/sheep.sh wool.txt
```

# Tips

Alternative, write script that uses parameters:

```text

├── data
│   └── seta
│       └── wool.txt
└── scripts
   └── sheep.sh

```

scripts/sheep.sh:
```bash
#!/bin/bash

file="$1"
sheep=$(grep sheep "$file" | wc -l)
echo "There are $sheep sheep in $file"
```
```bash
/scripts/sheep.sh data/seta/wool.txt
```

# Tips

Alternative, write script that uses pipes:

```text

├── data
│   └── seta
│       └── wool.txt
└── scripts
   └── sheep.sh

```

scripts/sheep.sh:
```bash
#!/bin/bash

sheep=$(grep sheep | wc -l)
echo "There are $sheep sheep."
```
```bash
cat data/seta/wool.txt | ./scripts/sheep.sh
```

# Tips
A script that reads which files to work on from a file

filesToWorkOn.txt
```
file1.txt
file2.txt
file3.txt
```

process.sh
```bash
#!/bin/bash

listFileName="$1"

filesToProcess=$(cat "$listFileName")

for fileToProcess in $filesToProcess
do
   echo "Processing file: $fileToProcess"
done
```
```
/process.sh filesToWorkOn.txt
Processing file: file1.txt
Processing file: file2.txt
Processing file: file3.txt
```


# Overview: Python

- Make a script & run it
- Variables & parameters
- Conditionals & loops
- Functions
- Reading & writing, files & pipes

# Why?!

- Easy and powerful math
- Easy and powerful text string manipulation
- Easier syntax
- Easier to work with sets and lists
- Nice modules for science stuff

# Make a script & run it

```bash
nano hello.py
```

```python
#!/usr/bin/python3
print("Hello World!")
```

- ctrl + o
- ctrl + x

```bash
/hello.py
Hello World!
```


# Variables

```python
#!/usr/bin/python3

placeToGreet = "Earth"

# Apologies, the f is not a typo, it tells python we want variables substituted in the text string
print(f"Hello World {placeToGreet}!")
```

# Variable

Types & objects

# Variables

- Text strings
- Numbers
- Lists
- Sets
- Dictionaries
- Powerful functions to easily work with them
- Convenient regex and sub-strings


# Variables

```python
# integer
length=23

# floating point
pi=3.1415
tau=pi*2

# set
instances={ pi, tau, tau, 'dog', 'viggo' }
# How many tau is in the set? 1. It's a set.
# What order are the items in? None, sets are unordered.

# Convenient for acumulating unique occurences of stuff
instances.add( 42 )

if tau in instances:
   print("Tau is in the set!")

# list
aList = [ 5,3,12,5 ]
# Lists are ordered, duplicates are allowed. (5 occurs twice, at the start and end)
# In python and most other languages, the first element of a list is 0
print(f"The second element in the list is {aList[1]})
```

# Variables

Dictionaries makes it easy to structure data

```python
dogs = dict(
   viggo = dict(
       age = 4,
       legs= 4,
       tail=true
   ),
   henry = dict(
       age = 9,
       legs= 3.9,
       tail=true
   )
)

dogs.update(
   bob = dict(
       age = 2,
       legs= 4,
       tail=false # poor guy!
   )
)

print( f"Viggo is {dogs['viggo']['age']} years old." )

```
# Variables

```python

# Sets can accumulate unique things, dicts can be count them
numAnimals = dict()

# -- snip --

if 'tau' not in numAnimals:
   numAnimals['tau'] = 0

numAnimals['tau'] += 1
```

# Variables

More than everything you need to know about Python variables at

[https://docs.python.org/3/library/stdtypes.html](https://docs.python.org/3/library/stdtypes.html)


# Conditionals

Conditionals easier to read in python, generally we don't run external programs, so the boolean result of an expression is all we worry about.

A condition is met when the result is True or any non-zero value.

A condition is not met when the result is False or 0, or empty (such as the empty string "" or list [])

# Conditionals

```python
a > b # Is True if a is greater than b
a < b # Is True if a is less than b
a == b # Is True if a is equal to b
a != b # Is true if a is not euqal t b
a < b and b > c # IS true if a is less than b AND b is greater than c
a > b or b < c # Is true if a is greater than b or if b is less than c
a in c # Is true if a is an element in c
a not in c # Is true if a is not an element in c
```

# Conditionals

```python
if CONDITION:
   # Do something that should be done
   # if CONDITION is met
```
```python
if CONDITION
   # Do something
else
   # Do something else
```

# Attention!
Python is indentation sensitive, that is, the number of "tabs" indicate to which code-block a line belong.

```python

if False:
   print("This is never shown")
   print("And this is also never shown")
print("This is shown")
```

# Conditionals

```python
name = 'viggo'

if name == 'viggo':
   print(f"The dogs name is Viggo!")
```
# Conditionals

```python
name = 'Viggo'

# 'Viggo' is not the same as 'viggo', but python strings provide a method for lowercasing for comparisons
if name.casefold() == 'viggo':
   print(f"The dogs name is Viggo!")
```

# Loops

```python
# For is great for iteration over "iterables"
for VARIABLE in ITERABLE:
   # do thing for each item
```

```python
for number in range( 10, 20 ):
   print(f"Number: {number}")
```
```
Number: 10
Number: 11
Number: 12
Number: 13
Number: 14
Number: 15
Number: 16
Number: 17
Number: 18
Number: 19
```
More about the CSV module at [https://docs.python.org/3/library/csv.html](https://docs.python.org/3/library/csv.html)

# Loops


```python
#!/usr/bin/python3

import sys
import csv

# This csv file used ; instead of ,
reader = csv.reader(sys.stdin, delimiter=';')

rowNumber=0

for row in reader:
   name=row[0]
   age=int(row[1]) # Convert from text to number
   print(f"Row {rowNumber}, name: {name} age: {age}")
   if age > 5:
       print(f"{name} is an old doggie!")
   rowNumber += 1
```
```bash
cat csvfi | ./hello.py
Row 1, name: viggo age: 6
viggo is an old doggie!
Row 2, name: henry age: 4
```

# Loops

Much more about loops here: [https://wiki.python.org/moin/ForLoop](https://wiki.python.org/moin/ForLoop)

# Reading from a file

With an open file, readline will read a single line from the file, useful for large files

```python
#!/usr/bin/python3

fileName="poe.txt"

with open(fileName) as textFile:
   while True:
       line = textFile.readline()
       if not line: # If the line was empty, file end reached
           break;   # so break the lop
       line = line.strip();
       print(f"{line}")
```
```
Once upon a midnight dreary,
while I pondered, weak and weary,
Over many a quaint and curious
volume of forgotten lore—
```

# Reading from a file

With an open file, readlines will read from the file into a list

```python
#!/usr/bin/python3

lineNumber=0
fileName="poe.txt"

with open(fileName) as textFile:
       lines = textFile.readlines()
       for line in lines:
           lineNumber += 1
           line = line.strip(); # remove newline from the line
           print(f"{fileName} {lineNumber}: {line}")
```

```
poe.txt 1: Once upon a midnight dreary,
poe.txt 2: while I pondered, weak and weary,
poe.txt 3: Over many a quaint and curious
poe.txt 4: volume of forgotten lore—
```


# Writing files

reverser.py
```python
#!/usr/bin/python3

inFileName="poe.txt"
outFileName="eop.txt"

with open(inFileName) as inFile:
   with open(outFileName, 'w') as outFile:
       while True:
           line = inFile.readline()
           if not line: # If the line was empty, file end reached
               break;   # so break the lop
           line = line.strip();
           line = line[::-1] # Reverse the string
           line = line + "\n"
           outFile.write(line)
```

```bash
/reverser.py
cat eop.txt
,yraerd thgindim a nopu ecnO
,yraew dna kaew ,derednop I elihw
suoiruc dna tniauq a ynam revO
```

# Writing CSV files

```python
#!/usr/bin/python3
import csv

ourResults = []
ourResults.append( ['Johnny', 'Cat', 4 ] )
ourResults.append( ['Viggo', 'Dog', 5 ] )
ourResults.append( ['Mat', 'Dog', 7 ] )
ourResults.append( ['Markus', 'Cat', 7 ] )

with open("out.csv", "w") as csvOutFile:
   writer = csv.writer(csvOutFile, dialect='excel')
   writer.writerow(['Name', 'Species', 'Age'])
   for row in ourResults:
       writer.writerow(row)
```
```bash
cat out.csv
Name,Species,Age
Johnny,Cat,4
Viggo,Dog,5
Mat,Dog,7
Markus,Cat,7
```

# Writing CSV files

```python
#!/usr/bin/python3
import csv

csvHeader = [ 'Name', 'Species', 'Age' ]

ourResults = []
ourResults.append( { 'Name': 'Johnny', 'Species': 'Cat', 'Age': 4 } )
ourResults.append( { 'Name': 'Viggo',  'Species': 'Dog', 'Age': 5 } )
ourResults.append( { 'Name': 'Mat',    'Species': 'Dog', 'Age': 7 } )
ourResults.append( { 'Name': 'Markus', 'Species': 'Cat', 'Age': 7 } )


with open("out.csv", "w") as csvOutFile:
   writer = csv.DictWriter(csvOutFile, fieldnames=csvHeader, dialect='excel')
   writer.writeheader()
   for row in ourResults:
       writer.writerow(row)

```
# String search

```python
string.find(KEYWORD)

#rfind is the same, but goes right to left

# They return -1 if KEYWORD not found
```

```python
needle = "is"
haystack = "This is a test"

position = haystack.find(needle)  # 2
position = haystack.rfind(needle) # 5
```

# String slicing

```python
string[ BEGIN : STEP : END ]
```

```python
# Slicing (extract substring)

myString = "abcdefg"

subString = myString[0:3:1] # First 3 characters (character 0,1,2)
subString = myString[3:6:1] # From third character to sixth (def)
substring = myString[-2::]  # Last 2 characters
subString = myString[::-1]  # Step through string in reverse
```

# String splitting
```python
string.split( DELIMITER )
string.split( DELIMITER, NUMBER_OF_SPLITS)

# rsplit is the same, but goes right to left
```
```python
myString = "a,b,c,d,e,f,g"

subString = myString.split(',') # ['a', 'b', 'c', 'd', 'e', 'f', 'g']
subString = myString.split(',', 1) # ['a', 'b,c,d,e,f,g']
subString = myString.rsplit(',', 1) # ['a,b,c,d,e,f', 'g']
```

# Regular expressions

```python
import re # regex module

text = "He was carefully disguised but captured quickly by police."

subString = re.split(r"but", text) # ['He was carefully disguised ', ' captured quickly by police.']

subString = re.findall(r"\w+ly\b", text) # ['carefully', 'quickly']
```

More about regex at [https://docs.python.org/3/library/re.html](https://docs.python.org/3/library/re.html)

# Searching in list

```python
#!/usr/bin/python3

listOfAnimals = [
       { "Name": "Viggo", "Species": "Dog", "Age": 5 },
       { "Name": "Mat", "Species": "Dog", "Age": 6 },
       { "Name": "Oliver", "Species": "Cat", "Age": 5 },
       { "Name": "Luggie", "Species": "Dog", "Age": 5 }
]

fiveYearOldDogs = []

for animal in listOfAnimals:
   if animal["Species"] == "Dog" and animal["Age"] == 5:
       fiveYearOldDogs.append(animal["Name"])

print(fiveYearOldDogs)
```
```
['Viggo', 'Luggie']
```

# Functions

```python
def FUN_NAME( ARGUMENTS ):
   BODY
```

# Functions

```python
#!/usr/bin/python3

# repeater will return a textToRepeat repeated timesToRepeat times.
# If timesToRepeat is not provided, a default value of 5 is used
def repeater( textToRepeat, timesToRepeat=5 ):
   return textToRepeat * timesToRepeat

repeatedString = repeater("test", 3)

print(repeatedString)

```
```
testtesttest

```

# Functions

```python
#!/usr/bin/python3

listOfAnimals = [
       { "Name": "Viggo", "Species": "Dog", "Age": 5 },
       { "Name": "Mat", "Species": "Dog", "Age": 6 },
       { "Name": "Oliver", "Species": "Cat", "Age": 5 },
       { "Name": "Luggie", "Species": "Dog", "Age": 5 }
]

def isLuggie( animal ):
   return animal["Species"] == "Dog" and animal["Age"] == 5 and animal["Name"] == "Luggie"

num = 0

for animal in listOfAnimals:
   num += 1
   if isLuggie(animal):
       print(f"Found {animal['Name']} he is number {num} in the list")

```
```
Found Luggie he is number 4 in the list
```

# Sorting lists

```python
list.sort()
list.sort( reverse=True )
list.sort( key=FUNCTION )
```

```python
#!/usr/bin/python3

myList = [ 2, 6, 1 ,4 ]
myList.sort() # Sort will sort the list "in place"
print(myList)

myList = [
   { 'Age': 5, 'Name': 'Viggo' },
   { 'Age' : 2, 'Name': 'Mat' }
]

def getAge(animal):
   return animal['Age']

myList.sort( key=getAge)

print(myList)
```
```
[1, 2, 4, 6]
[{'Age': 2, 'Name': 'Mat'}, {'Age': 5, 'Name': 'Viggo'}]
```

# The end

![](./end.png)