๐ฏ FP Patterns in R: Your Magic Toolbox
The Story of the Magical Assembly Line
Imagine you work in a toy factory. Every day, hundreds of toy parts come down a conveyor belt. You need to:
- Transform each part (paint it, add wheels)
- Filter out broken ones
- Combine all parts into finished toys
In R, we have three magical tools that do exactly this โ but for data!
๐บ๏ธ The Map Function: The Transformer
What is it?
Think of map like a magic wand that touches every item in a box and transforms it the same way.
Real-life example:
- You have 5 plain cupcakes
- You wave your wand (the frosting function)
- Now you have 5 frosted cupcakes!
How it works in R
# Our cupcakes (numbers)
cupcakes <- c(1, 2, 3, 4, 5)
# Wave our magic wand (double each)
frosted <- Map(function(x) x * 2, cupcakes)
# Result: 2, 4, 6, 8, 10
The Pattern
Map(what_to_do, list_of_things)
- what_to_do = Your magic spell (a function)
- list_of_things = Items to transform
Another Example: Making Names Fancy
names <- list("ana", "bob", "cat")
fancy <- Map(toupper, names)
# Result: "ANA", "BOB", "CAT"
Key Insight: Map returns a list. Every item goes in, transformed item comes out. Same count!
๐ The Filter Function: The Gatekeeper
What is it?
Imagine a bouncer at a party. They check each guest and only let in those who meet the rules.
Real-life example:
- 10 kids want to ride the roller coaster
- Rule: Must be taller than 4 feet
- Filter checks each kid
- Only 6 kids get through!
How it works in R
# Kids' heights in feet
heights <- c(3.5, 4.2, 3.8, 5.0, 4.5)
# The bouncer's rule
tall_enough <- function(h) h >= 4
# Check everyone
riders <- Filter(tall_enough, heights)
# Result: 4.2, 5.0, 4.5
The Pattern
Filter(the_rule, list_of_things)
- the_rule = TRUE means โlet inโ, FALSE means โrejectโ
- list_of_things = Everyone waiting in line
Finding Even Numbers
numbers <- c(1, 2, 3, 4, 5, 6, 7, 8)
is_even <- function(n) n %% 2 == 0
evens <- Filter(is_even, numbers)
# Result: 2, 4, 6, 8
Key Insight: Filter makes the list smaller (or same size). It never adds items!
โ The Reduce Function: The Combiner
What is it?
Imagine youโre making a snowball. You start small, then roll it through snow โ it picks up more and more until you have one big ball!
Real-life example:
- You have coins: 25ยข, 10ยข, 5ยข, 25ยข
- You add them one by one
- End result: 65ยข (one number!)
How it works in R
# Our coins in cents
coins <- c(25, 10, 5, 25)
# Roll them together (add)
total <- Reduce(`+`, coins)
# Result: 65
The Magic Behind Reduce
Step 1: 25 + 10 = 35
Step 2: 35 + 5 = 40
Step 3: 40 + 25 = 65
Reduce takes two items at a time, combines them, then takes the result and combines it with the next item.
The Pattern
Reduce(how_to_combine, list_of_things)
Finding the Biggest Number
scores <- c(85, 92, 78, 95, 88)
biggest <- Reduce(max, scores)
# Result: 95
Building a Sentence
words <- c("I", "love", "R")
sentence <- Reduce(paste, words)
# Result: "I love R"
Key Insight: Reduce always returns ONE thing โ many become one!
๐ The do.call Function: The Phone Operator
What is it?
Imagine a phone operator who receives:
- A phone number (which function to call)
- A message (the arguments)
The operator dials the number and delivers your message!
The Problem It Solves
Sometimes you have arguments stored in a list, but the function wants them separately.
# You have this:
my_args <- list(1, 2, 3, 4, 5)
# sum() wants: sum(1, 2, 3, 4, 5)
# NOT: sum(list(1, 2, 3, 4, 5))
do.call to the Rescue!
my_args <- list(1, 2, 3, 4, 5)
# The operator unpacks and delivers!
result <- do.call(sum, my_args)
# Result: 15
The Pattern
do.call(function_name, list_of_arguments)
Real Example: Combining Data Frames
# Three separate tables
df1 <- data.frame(x = 1:2)
df2 <- data.frame(x = 3:4)
df3 <- data.frame(x = 5:6)
# Pack them in a list
all_dfs <- list(df1, df2, df3)
# Operator calls rbind with all three!
big_df <- do.call(rbind, all_dfs)
Result: One data frame with all rows combined!
Why Not Just Call the Function?
# This works:
sum(1, 2, 3)
# But what if args are in a list?
args <- list(1, 2, 3)
sum(args) # WRONG! Gives error
# do.call unpacks it:
do.call(sum, args) # Correct! Returns 6
Key Insight: do.call is your argument unpacker โ it spreads a list into separate arguments!
๐ Putting It All Together
Letโs use all four in one example!
Story: Youโre a teacher with test scores. You need to:
- Give bonus points to everyone (Map)
- Keep only passing scores (Filter)
- Find the class average (Reduce + do.call)
# Original scores
scores <- list(65, 72, 58, 85, 45, 90)
# Step 1: Map - Give 5 bonus points
boosted <- Map(function(s) s + 5, scores)
# Result: 70, 77, 63, 90, 50, 95
# Step 2: Filter - Keep passing (>= 60)
passing <- Filter(function(s) s >= 60, boosted)
# Result: 70, 77, 63, 90, 95
# Step 3: Reduce - Sum all scores
total <- Reduce(`+`, passing)
# Result: 395
# Step 4: do.call - Calculate average
avg <- do.call(`/`, list(total, length(passing)))
# Result: 79
๐ Quick Comparison
graph TD A["Your Data"] --> B{What do you need?} B -->|Transform each item| C["Map"] B -->|Keep some items| D["Filter"] B -->|Combine into one| E["Reduce"] B -->|Unpack list to args| F["do.call"] C --> G["Same count out"] D --> H["Fewer items out"] E --> I["One thing out"] F --> J["Calls function properly"]
๐ฎ Memory Tricks
| Function | Remember As | Input โ Output |
|---|---|---|
| Map | Magic wand โจ | 5 items โ 5 transformed items |
| Filter | Bouncer ๐ช | 10 items โ fewer items |
| Reduce | Snowball โ | Many items โ 1 result |
| do.call | Phone operator ๐ | List โ unpacked arguments |
๐ก Why Use FP Patterns?
- Cleaner code โ No messy for-loops
- Fewer bugs โ Each function does ONE thing
- Easier to read โ Says WHAT, not HOW
- Reusable โ Same patterns everywhere
Before (messy):
result <- c()
for (i in 1:length(nums)) {
result <- c(result, nums[i] * 2)
}
After (clean):
result <- Map(function(x) x * 2, nums)
๐ You Did It!
You now understand the four magic tools of functional programming in R:
- ๐บ๏ธ Map โ Transform everything
- ๐ Filter โ Keep the good ones
- โ Reduce โ Combine into one
- ๐ do.call โ Unpack and call
These patterns will make your R code shorter, cleaner, and more powerful!
Happy coding! ๐
