Skip to main content

šŸ”§ Lesson 9: Array Functions

PHP ships with over 80 built-in array functions — a powerful toolkit for transforming, filtering, sorting, and combining data. In this lesson you'll master the ones you'll reach for every day.

šŸŽÆ Learning Objectives

By the end of this lesson, you will be able to:

  • Transform arrays with array_map()
  • Filter arrays with array_filter()
  • Reduce arrays to a single value with array_reduce()
  • Sort arrays by value and key in multiple directions
  • Merge, combine, slice, and splice arrays
  • Search, extract, and manipulate array data with utility functions
  • Chain these functions together to build data pipelines

Estimated Time: 45 minutes

Prerequisites: Lesson 8 (arrays, foreach, associative arrays, arrow functions)

šŸ“‘ In This Lesson

The Big Three: map, filter, reduce

These three functions are the workhorses of array processing. Together they can handle the vast majority of data transformation tasks.

flowchart LR A["Input Array"] --> B["array_map
Transform each element"] A --> C["array_filter
Keep matching elements"] A --> D["array_reduce
Collapse to single value"] B --> E["New array
(same length)"] C --> F["New array
(fewer elements)"] D --> G["Single value
(number, string, etc.)"]

array_map() — Transform Every Element

šŸ“– Definition

array_map(callable $callback, array $array): array — Applies a function to every element and returns a new array of the results. The original array is not modified.

<?php
$prices = [10.99, 24.50, 7.25, 18.00];

// Apply 10% discount to all prices
$discounted = array_map(fn($p) => round($p * 0.90, 2), $prices);
// [9.89, 22.05, 6.53, 16.2]

// Convert all strings to uppercase
$names = ["alice", "bob", "carol"];
$upper = array_map('strtoupper', $names);
// ["ALICE", "BOB", "CAROL"]

// Transform associative array values
$user = ["name" => "Alice", "city" => "Portland"];
$trimmed = array_map('trim', $user);
// Keys are preserved!

// With a more complex transformation
$products = ["widget", "gadget", "doohickey"];
$formatted = array_map(fn($name, $i) => ($i + 1) . ". " . ucfirst($name),
    $products, array_keys($products));
// ["1. Widget", "2. Gadget", "3. Doohickey"]
šŸ’” Note: Notice that the callback comes first, then the array. This is the opposite order from JavaScript's array.map(). It trips up a lot of developers switching between languages.

array_filter() — Keep Only Matching Elements

šŸ“– Definition

array_filter(array $array, ?callable $callback, int $mode): array — Returns a new array containing only the elements for which the callback returns true. Keys are preserved.

<?php
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Keep only even numbers
$evens = array_filter($numbers, fn($n) => $n % 2 === 0);
// [1 => 2, 3 => 4, 5 => 6, 7 => 8, 9 => 10]
// āš ļø Notice: original keys are preserved!

// Re-index if you need clean 0-based keys
$evens = array_values(array_filter($numbers, fn($n) => $n % 2 === 0));
// [2, 4, 6, 8, 10]

// Filter strings by length
$words = ["hi", "hello", "hey", "greetings", "yo"];
$long = array_filter($words, fn($w) => strlen($w) > 3);
// [1 => "hello", 3 => "greetings"]

// Filter associative arrays
$employees = [
    ["name" => "Alice", "salary" => 95000, "dept" => "Engineering"],
    ["name" => "Bob",   "salary" => 72000, "dept" => "Marketing"],
    ["name" => "Carol", "salary" => 105000, "dept" => "Engineering"],
    ["name" => "Dave",  "salary" => 68000, "dept" => "Marketing"],
];

$engineers = array_filter($employees, fn($e) => $e["dept"] === "Engineering");
$highEarners = array_filter($employees, fn($e) => $e["salary"] > 80000);

Filtering Without a Callback

With no callback, array_filter() removes all "falsy" values:

<?php
$data = ["hello", "", 0, null, false, "world", 42, [], "0"];

$cleaned = array_filter($data);
// ["hello", "world", 42]
// Removed: "", 0, null, false, [], "0"

āš ļø Careful with Falsy Filtering

Using array_filter() without a callback removes 0, "0", "", null, false, and []. If 0 is a valid value in your data (like a score or a count), write an explicit callback: fn($v) => $v !== null && $v !== "".

Filtering by Key

<?php
$data = [
    "name"    => "Alice",
    "email"   => "alice@example.com",
    "temp_id" => "abc123",
    "age"     => 30,
    "temp_token" => "xyz789",
];

// ARRAY_FILTER_USE_KEY — callback receives the key
$cleaned = array_filter($data, fn($key) => !str_starts_with($key, "temp_"),
    ARRAY_FILTER_USE_KEY);
// ["name" => "Alice", "email" => "alice@example.com", "age" => 30]

// ARRAY_FILTER_USE_BOTH — callback receives value AND key
$result = array_filter($data, fn($val, $key) => is_string($val) && $key !== "temp_id",
    ARRAY_FILTER_USE_BOTH);

array_reduce() — Collapse to a Single Value

šŸ“– Definition

array_reduce(array $array, callable $callback, mixed $initial): mixed — Iterates through the array, passing an accumulator and each element to the callback. Returns the final accumulated value.

<?php
$numbers = [1, 2, 3, 4, 5];

// Sum all numbers
$sum = array_reduce($numbers, fn($carry, $n) => $carry + $n, 0);
echo $sum; // 15

// How it works step by step:
// carry=0, n=1 → 0+1 = 1
// carry=1, n=2 → 1+2 = 3
// carry=3, n=3 → 3+3 = 6
// carry=6, n=4 → 6+4 = 10
// carry=10, n=5 → 10+5 = 15

// Product of all numbers
$product = array_reduce($numbers, fn($carry, $n) => $carry * $n, 1);
echo $product; // 120

// Find the longest string
$words = ["cat", "elephant", "dog", "hippopotamus", "ant"];
$longest = array_reduce($words,
    fn($carry, $word) => strlen($word) > strlen($carry) ? $word : $carry,
    ""
);
echo $longest; // "hippopotamus"

// Build a string
$parts = ["Hello", "World", "from", "PHP"];
$sentence = array_reduce($parts,
    fn($carry, $word) => $carry === "" ? $word : "$carry $word",
    ""
);
echo $sentence; // "Hello World from PHP"

Reduce with Associative Arrays

<?php
$cart = [
    ["name" => "Widget",  "price" => 12.99, "qty" => 3],
    ["name" => "Gadget",  "price" => 49.99, "qty" => 1],
    ["name" => "Cable",   "price" => 7.50,  "qty" => 2],
];

// Calculate total cost
$total = array_reduce($cart,
    fn($sum, $item) => $sum + ($item["price"] * $item["qty"]),
    0.0
);
echo "Total: \$" . number_format($total, 2); // Total: $103.96

// Count total items
$itemCount = array_reduce($cart, fn($count, $item) => $count + $item["qty"], 0);
echo "Items: $itemCount"; // Items: 6

// Build a summary string
$summary = array_reduce($cart,
    fn($carry, $item) => $carry . "{$item['name']} Ɨ{$item['qty']}, ",
    ""
);
echo rtrim($summary, ", "); // Widget Ɨ3, Gadget Ɨ1, Cable Ɨ2

Reduce to Build Arrays (Advanced)

<?php
// Group by category
$products = [
    ["name" => "Widget",    "category" => "Tools"],
    ["name" => "Gadget",    "category" => "Electronics"],
    ["name" => "Wrench",    "category" => "Tools"],
    ["name" => "Tablet",    "category" => "Electronics"],
    ["name" => "Hammer",    "category" => "Tools"],
];

$grouped = array_reduce($products, function($carry, $product) {
    $cat = $product["category"];
    $carry[$cat][] = $product["name"];
    return $carry;
}, []);

print_r($grouped);
/* Array (
    [Tools] => Array ( [0] => Widget [1] => Wrench [2] => Hammer )
    [Electronics] => Array ( [0] => Gadget [1] => Tablet )
) */

āœ… When to Use Each

array_map: "I want the same number of items, but each one transformed."

array_filter: "I want fewer items — only those matching a condition."

array_reduce: "I want to collapse everything into one value — a total, a string, a grouped structure."

Sorting Arrays

PHP provides a family of sorting functions. The key distinction is whether the function preserves keys, and whether it sorts by value or by key.

āš ļø Sort Functions Modify In-Place

All PHP sort functions modify the original array directly and return true on success — they do not return a new sorted array. This is different from many other languages.

Sorting Indexed Arrays

<?php
$numbers = [3, 1, 4, 1, 5, 9, 2, 6];

// sort — ascending, re-indexes keys
sort($numbers);
// [1, 1, 2, 3, 4, 5, 6, 9]

// rsort — descending, re-indexes keys
rsort($numbers);
// [9, 6, 5, 4, 3, 2, 1, 1]

// Strings sort alphabetically
$fruits = ["cherry", "apple", "banana", "date"];
sort($fruits);
// ["apple", "banana", "cherry", "date"]

Sorting Associative Arrays

<?php
$ages = ["Alice" => 30, "Carol" => 25, "Bob" => 35, "Dave" => 28];

// asort — sort by VALUE, PRESERVE keys
asort($ages);
// ["Carol" => 25, "Dave" => 28, "Alice" => 30, "Bob" => 35]

// arsort — sort by value descending, preserve keys
arsort($ages);
// ["Bob" => 35, "Alice" => 30, "Dave" => 28, "Carol" => 25]

// ksort — sort by KEY ascending
ksort($ages);
// ["Alice" => 30, "Bob" => 35, "Carol" => 25, "Dave" => 28]

// krsort — sort by key descending
krsort($ages);
// ["Dave" => 28, "Carol" => 25, "Bob" => 35, "Alice" => 30]

Sorting Reference Table

Function Sort By Order Preserves Keys? Best For
sort() Value Ascending āŒ Re-indexes Simple indexed arrays
rsort() Value Descending āŒ Re-indexes Simple indexed arrays
asort() Value Ascending āœ… Yes Associative arrays
arsort() Value Descending āœ… Yes Associative arrays
ksort() Key Ascending āœ… Yes Sort by key name
krsort() Key Descending āœ… Yes Sort by key name
usort() Custom Custom āŒ Re-indexes Complex sorting logic
uasort() Custom Custom āœ… Yes Custom + preserve keys
uksort() Custom key Custom āœ… Yes Custom key sorting

Custom Sorting with usort()

The usort() function lets you define your own comparison logic with a callback. The callback receives two elements and must return a negative number, zero, or positive number:

<?php
$employees = [
    ["name" => "Carol", "salary" => 105000],
    ["name" => "Alice", "salary" => 95000],
    ["name" => "Dave",  "salary" => 68000],
    ["name" => "Bob",   "salary" => 72000],
];

// Sort by salary ascending (spaceship operator!)
usort($employees, fn($a, $b) => $a["salary"] <=> $b["salary"]);
// Dave (68k), Bob (72k), Alice (95k), Carol (105k)

// Sort by salary descending — just flip $a and $b
usort($employees, fn($a, $b) => $b["salary"] <=> $a["salary"]);
// Carol (105k), Alice (95k), Bob (72k), Dave (68k)

// Sort by name alphabetically
usort($employees, fn($a, $b) => strcmp($a["name"], $b["name"]));
// or: fn($a, $b) => $a["name"] <=> $b["name"]

// Multi-level sort: by department, then by salary descending
$employees = [
    ["name" => "Alice", "dept" => "Engineering", "salary" => 95000],
    ["name" => "Bob",   "dept" => "Marketing",   "salary" => 72000],
    ["name" => "Carol", "dept" => "Engineering", "salary" => 105000],
    ["name" => "Dave",  "dept" => "Marketing",   "salary" => 68000],
];

usort($employees, function($a, $b) {
    // First compare by department
    $deptCompare = $a["dept"] <=> $b["dept"];
    if ($deptCompare !== 0) return $deptCompare;

    // If same department, sort by salary descending
    return $b["salary"] <=> $a["salary"];
});
// Carol (Eng, 105k), Alice (Eng, 95k), Bob (Mkt, 72k), Dave (Mkt, 68k)

āœ… The Spaceship Operator (<=>)

The <=> operator compares two values and returns -1, 0, or 1 — exactly what sorting callbacks need. It works with numbers, strings, and even arrays. For descending order, just swap $a and $b.

Merging & Combining

array_merge() — Combine Arrays

<?php
// Indexed arrays — values are appended, keys are re-indexed
$a = [1, 2, 3];
$b = [4, 5, 6];
$merged = array_merge($a, $b);
// [1, 2, 3, 4, 5, 6]

// Associative arrays — later values override
$defaults = ["theme" => "light", "lang" => "en", "page_size" => 20];
$prefs    = ["theme" => "dark", "lang" => "es"];
$config   = array_merge($defaults, $prefs);
// ["theme" => "dark", "lang" => "es", "page_size" => 20]

// Merge many arrays at once
$all = array_merge($a, $b, [7, 8], [9, 10]);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

array_merge vs. Spread Operator

Feature array_merge() Spread [...$a, ...$b]
Indexed arrays Same behavior Same behavior
Associative arrays Same behavior Same (PHP 8.1+)
Add inline values Wrap in array first Just add them: [...$a, "new"]
PHP version All versions 7.4+ (indexed), 8.1+ (assoc)

array_combine() — Keys + Values → One Array

<?php
$keys   = ["name", "email", "age"];
$values = ["Alice", "alice@example.com", 30];

$user = array_combine($keys, $values);
// ["name" => "Alice", "email" => "alice@example.com", "age" => 30]

// Useful when processing CSV headers
$headers = ["Product", "Price", "Stock"];
$row     = ["Widget", 12.99, 50];
$product = array_combine($headers, $row);
// ["Product" => "Widget", "Price" => 12.99, "Stock" => 50]

array_zip / Pairing Arrays

<?php
// array_map with null callback zips arrays together
$names  = ["Alice", "Bob", "Carol"];
$scores = [95, 87, 92];

$pairs = array_map(null, $names, $scores);
// [["Alice", 95], ["Bob", 87], ["Carol", 92]]

array_unique() — Remove Duplicates

<?php
$colors = ["red", "blue", "green", "red", "blue", "yellow"];

$unique = array_unique($colors);
// [0 => "red", 1 => "blue", 2 => "green", 5 => "yellow"]

// Re-index for clean keys
$unique = array_values(array_unique($colors));
// ["red", "blue", "green", "yellow"]

array_diff() and array_intersect()

<?php
$a = [1, 2, 3, 4, 5];
$b = [3, 4, 5, 6, 7];

// Values in $a that are NOT in $b
$diff = array_diff($a, $b);
// [0 => 1, 1 => 2]

// Values that exist in BOTH $a and $b
$common = array_intersect($a, $b);
// [2 => 3, 3 => 4, 4 => 5]

// Works with strings too
$mySkills   = ["PHP", "JavaScript", "Python", "SQL"];
$jobRequires = ["PHP", "SQL", "React", "Docker"];

$matching = array_intersect($mySkills, $jobRequires);
// ["PHP", "SQL"]

$needToLearn = array_diff($jobRequires, $mySkills);
// ["React", "Docker"]

Slicing & Splicing

array_slice() — Extract a Portion

<?php
$letters = ["a", "b", "c", "d", "e", "f"];

// From index 2, take 3 elements
$slice = array_slice($letters, 2, 3);
// ["c", "d", "e"]

// From index 1 to end
$slice = array_slice($letters, 1);
// ["b", "c", "d", "e", "f"]

// Last 2 elements (negative offset = from end)
$slice = array_slice($letters, -2);
// ["e", "f"]

// First 3 elements
$slice = array_slice($letters, 0, 3);
// ["a", "b", "c"]

// Preserve keys with the 4th parameter
$slice = array_slice($letters, 2, 3, true);
// [2 => "c", 3 => "d", 4 => "e"]

array_splice() — Remove/Replace a Portion (In-Place)

<?php
$colors = ["red", "green", "blue", "yellow", "purple"];

// Remove 2 elements starting at index 1
$removed = array_splice($colors, 1, 2);
echo implode(", ", $removed); // "green, blue"
echo implode(", ", $colors);  // "red, yellow, purple"

// Replace elements
$colors = ["red", "green", "blue", "yellow"];
array_splice($colors, 1, 2, ["teal", "pink", "gold"]);
// ["red", "teal", "pink", "gold", "yellow"]

// Insert without removing (length = 0)
$colors = ["red", "blue", "green"];
array_splice($colors, 1, 0, ["orange"]);
// ["red", "orange", "blue", "green"]

array_chunk() — Split into Groups

<?php
$items = [1, 2, 3, 4, 5, 6, 7, 8];

// Split into chunks of 3
$chunks = array_chunk($items, 3);
// [[1, 2, 3], [4, 5, 6], [7, 8]]

// Preserve keys
$chunks = array_chunk($items, 3, true);
// [[0=>1, 1=>2, 2=>3], [3=>4, 4=>5, 5=>6], [6=>7, 7=>8]]

// Practical use: pagination
$allProducts = [...]; // 100 products
$pages = array_chunk($allProducts, 10);
$page3 = $pages[2]; // Products 21-30

Searching & Extracting

array_column() — Extract a Column

Pulls out one field from every record in a list — incredibly useful with database results:

<?php
$employees = [
    ["name" => "Alice", "email" => "alice@example.com", "dept" => "Engineering"],
    ["name" => "Bob",   "email" => "bob@example.com",   "dept" => "Marketing"],
    ["name" => "Carol", "email" => "carol@example.com", "dept" => "Engineering"],
];

// Get all names
$names = array_column($employees, "name");
// ["Alice", "Bob", "Carol"]

// Get all emails
$emails = array_column($employees, "email");
// ["alice@example.com", "bob@example.com", "carol@example.com"]

// Index results by a field
$byName = array_column($employees, null, "name");
// ["Alice" => ["name"=>"Alice", ...], "Bob" => [...], "Carol" => [...]]

// Get emails indexed by name
$emailByName = array_column($employees, "email", "name");
// ["Alice" => "alice@example.com", "Bob" => "bob@example.com", ...]

āœ… array_column() Is a Superpower

Before array_column(), extracting one field from a list of records required a foreach loop or array_map(). Now it's one function call. Use it every time you need to pull a single field from an array of associative arrays.

array_keys() and array_values()

<?php
$user = ["name" => "Alice", "age" => 30, "city" => "Portland"];

// Get all keys
$keys = array_keys($user);
// ["name", "age", "city"]

// Get all values (also re-indexes numeric arrays)
$values = array_values($user);
// ["Alice", 30, "Portland"]

// Find keys where value matches
$scores = ["Alice" => 95, "Bob" => 87, "Carol" => 95, "Dave" => 73];
$topKeys = array_keys($scores, 95);
// ["Alice", "Carol"] — all keys with value 95

array_flip() — Swap Keys and Values

<?php
$countryCodes = ["US" => "United States", "GB" => "United Kingdom", "JP" => "Japan"];

$flipped = array_flip($countryCodes);
// ["United States" => "US", "United Kingdom" => "GB", "Japan" => "JP"]

// Useful for quick lookups — turn a list into a set-like structure
$allowedRoles = ["admin", "editor", "viewer"];
$roleSet = array_flip($allowedRoles);
// ["admin" => 0, "editor" => 1, "viewer" => 2]

// isset() on flipped array is faster than in_array() for large arrays
if (isset($roleSet["admin"])) {
    echo "Valid role!\n";
}

array_count_values() — Frequency Counter

<?php
$votes = ["red", "blue", "red", "green", "blue", "red", "blue", "blue"];

$tally = array_count_values($votes);
// ["red" => 3, "blue" => 4, "green" => 1]

arsort($tally); // Sort by count descending
foreach ($tally as $color => $count) {
    echo "$color: $count votes\n";
}
// blue: 4 votes
// red: 3 votes
// green: 1 vote

Utility Functions

array_reverse()

<?php
$numbers = [1, 2, 3, 4, 5];
$reversed = array_reverse($numbers);
// [5, 4, 3, 2, 1]

array_pad() — Pad to a Length

<?php
$data = [1, 2, 3];

// Pad to 6 elements with 0
$padded = array_pad($data, 6, 0);
// [1, 2, 3, 0, 0, 0]

// Pad to the left (negative length)
$padded = array_pad($data, -6, 0);
// [0, 0, 0, 1, 2, 3]

array_fill() and range()

<?php
// Fill an array with a value
$zeros = array_fill(0, 5, 0);
// [0, 0, 0, 0, 0]

$defaults = array_fill(0, 3, ["score" => 0, "attempts" => 0]);
// [["score"=>0, "attempts"=>0], ["score"=>0, "attempts"=>0], ...]

// Generate a range of values
$nums = range(1, 10);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

$evens = range(0, 20, 2); // Step of 2
// [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

$letters = range('a', 'z');
// ["a", "b", "c", ..., "z"]

implode() and explode()

These convert between arrays and strings (covered fully in Lesson 10, but essential for arrays too):

<?php
// implode — array → string
$words = ["Hello", "World", "from", "PHP"];
$sentence = implode(" ", $words);
// "Hello World from PHP"

$csv = implode(",", [1, 2, 3, 4, 5]);
// "1,2,3,4,5"

// explode — string → array
$data = "Alice,Bob,Carol,Dave";
$names = explode(",", $data);
// ["Alice", "Bob", "Carol", "Dave"]

compact() and extract()

<?php
// compact — variables → associative array
$name = "Alice";
$age = 30;
$city = "Portland";

$user = compact("name", "age", "city");
// ["name" => "Alice", "age" => 30, "city" => "Portland"]

// extract — associative array → variables
$config = ["theme" => "dark", "language" => "en", "debug" => true];
extract($config);
echo $theme;    // "dark"
echo $language; // "en"
echo $debug;    // true (1)

āš ļø Be Careful with extract()

extract() creates variables from array keys, which can overwrite existing variables. Never use it with untrusted data (like $_GET or $_POST). It's mostly useful in controlled situations like template rendering. Destructuring (["key" => $var] = $arr) is generally safer since you explicitly name what you're extracting.

Building Data Pipelines

The real power of array functions emerges when you chain them together. Each step transforms the data, feeding its output into the next function.

Example: E-Commerce Report

<?php
$orders = [
    ["id" => 101, "product" => "Widget",    "price" => 12.99, "qty" => 3, "status" => "shipped"],
    ["id" => 102, "product" => "Gadget",    "price" => 49.99, "qty" => 1, "status" => "pending"],
    ["id" => 103, "product" => "Cable",     "price" => 7.50,  "qty" => 5, "status" => "shipped"],
    ["id" => 104, "product" => "Doohickey", "price" => 8.50,  "qty" => 2, "status" => "cancelled"],
    ["id" => 105, "product" => "Gizmo",     "price" => 31.00, "qty" => 1, "status" => "shipped"],
    ["id" => 106, "product" => "Thingamajig","price" => 22.00,"qty" => 4, "status" => "pending"],
];

// Pipeline: Filter → Transform → Sort → Reduce

// Step 1: Only shipped orders
$shipped = array_filter($orders, fn($o) => $o["status"] === "shipped");

// Step 2: Add a "total" field to each
$withTotals = array_map(fn($o) => [
    ...$o,
    "total" => $o["price"] * $o["qty"],
], $shipped);

// Step 3: Sort by total descending
usort($withTotals, fn($a, $b) => $b["total"] <=> $a["total"]);

// Step 4: Calculate grand total
$grandTotal = array_reduce($withTotals,
    fn($sum, $o) => $sum + $o["total"], 0.0);

// Display
echo "šŸ“¦ Shipped Orders Report\n";
echo str_repeat("=", 50) . "\n";
foreach ($withTotals as $order) {
    echo sprintf("  #%d %-12s %d Ɨ \$%5.2f = \$%6.2f\n",
        $order["id"], $order["product"],
        $order["qty"], $order["price"], $order["total"]);
}
echo str_repeat("-", 50) . "\n";
echo sprintf("  Grand Total: %30s\$%6.2f\n", "", $grandTotal);

Output:

šŸ“¦ Shipped Orders Report
==================================================
  #101 Widget       3 Ɨ $12.99 = $ 38.97
  #105 Cable        5 Ɨ $ 7.50 = $ 37.50
  #103 Gizmo        1 Ɨ $31.00 = $ 31.00
--------------------------------------------------
  Grand Total:                        $107.47

Example: Word Frequency Analyzer

<?php
$text = "the quick brown fox jumps over the lazy dog the fox the dog";

// Pipeline: split → lowercase → count → sort → top 5
$words = explode(" ", strtolower($text));                    // Split into words
$counts = array_count_values($words);                         // Count occurrences
arsort($counts);                                              // Sort by count descending
$top5 = array_slice($counts, 0, 5, true);                    // Take top 5

echo "Top 5 words:\n";
foreach ($top5 as $word => $count) {
    $bar = str_repeat("ā–ˆ", $count);
    echo sprintf("  %-8s %s (%d)\n", $word, $bar, $count);
}

āœ… Pipeline Thinking

When faced with a complex data task, break it down into steps: Filter what you need → Transform each item → Sort the results → Reduce to a summary. Each step is a simple function call, and the data flows from one to the next.

Hands-On Exercises

šŸ‹ļø Exercise 1: Student Score Analyzer

Objective: Use array functions to analyze student performance data.

Given data:

$students = [
    ["name" => "Alice", "math" => 92, "science" => 88, "english" => 95],
    ["name" => "Bob",   "math" => 78, "science" => 85, "english" => 72],
    ["name" => "Carol", "math" => 95, "science" => 92, "english" => 89],
    ["name" => "Dave",  "math" => 65, "science" => 70, "english" => 68],
    ["name" => "Eve",   "math" => 88, "science" => 94, "english" => 91],
];

Tasks:

  1. Use array_map to add an "average" field to each student
  2. Use array_filter to find students with average ≄ 85 (honor roll)
  3. Use usort to sort by average descending
  4. Use array_column to extract just the names of honor roll students
  5. Use array_reduce to find the class average across all students
šŸ’” Hint

Calculate each student's average as ($math + $science + $english) / 3. Use the spread operator to add the average field: [...$student, "average" => ...].

āœ… Solution
<?php
$students = [
    ["name" => "Alice", "math" => 92, "science" => 88, "english" => 95],
    ["name" => "Bob",   "math" => 78, "science" => 85, "english" => 72],
    ["name" => "Carol", "math" => 95, "science" => 92, "english" => 89],
    ["name" => "Dave",  "math" => 65, "science" => 70, "english" => 68],
    ["name" => "Eve",   "math" => 88, "science" => 94, "english" => 91],
];

// 1. Add average field
$withAvg = array_map(fn($s) => [
    ...$s,
    "average" => round(($s["math"] + $s["science"] + $s["english"]) / 3, 1),
], $students);

// 2. Filter honor roll (average >= 85)
$honorRoll = array_filter($withAvg, fn($s) => $s["average"] >= 85);

// 3. Sort by average descending
usort($honorRoll, fn($a, $b) => $b["average"] <=> $a["average"]);

// 4. Extract names
$honorNames = array_column($honorRoll, "name");
echo "šŸ† Honor Roll: " . implode(", ", $honorNames) . "\n";

// 5. Class average
$classAvg = array_reduce($withAvg,
    fn($sum, $s) => $sum + $s["average"], 0.0) / count($withAvg);
echo "šŸ“Š Class Average: " . round($classAvg, 1) . "\n";

// Display full report
echo "\nšŸ“‹ Full Rankings:\n";
usort($withAvg, fn($a, $b) => $b["average"] <=> $a["average"]);
foreach ($withAvg as $i => $s) {
    $badge = $s["average"] >= 85 ? "⭐" : "  ";
    echo sprintf("  %s %d. %-8s Math:%3d Sci:%3d Eng:%3d Avg:%5.1f\n",
        $badge, $i + 1, $s["name"],
        $s["math"], $s["science"], $s["english"], $s["average"]);
}

šŸ‹ļø Exercise 2: Inventory Manager

Objective: Build an inventory processing system using array functions.

Given data:

$inventory = [
    ["sku" => "W001", "name" => "Widget",      "price" => 12.99, "stock" => 150, "category" => "Tools"],
    ["sku" => "G001", "name" => "Gadget",       "price" => 49.99, "stock" => 30,  "category" => "Electronics"],
    ["sku" => "C001", "name" => "Cable",        "price" => 7.50,  "stock" => 500, "category" => "Electronics"],
    ["sku" => "D001", "name" => "Doohickey",    "price" => 8.50,  "stock" => 0,   "category" => "Tools"],
    ["sku" => "T001", "name" => "Thingamajig",  "price" => 22.00, "stock" => 75,  "category" => "Toys"],
    ["sku" => "G002", "name" => "Gizmo",        "price" => 31.00, "stock" => 45,  "category" => "Electronics"],
    ["sku" => "W002", "name" => "Whatchamacallit","price"=> 15.75, "stock" => 200, "category" => "Toys"],
];

Tasks:

  1. Find all out-of-stock items (stock === 0)
  2. Calculate total inventory value (price Ɨ stock for each in-stock item)
  3. Group items by category and show count per category
  4. Find the most expensive item in each category
  5. Create a price-sorted product list with formatted prices
āœ… Solution
<?php
$inventory = [
    ["sku" => "W001", "name" => "Widget",      "price" => 12.99, "stock" => 150, "category" => "Tools"],
    ["sku" => "G001", "name" => "Gadget",       "price" => 49.99, "stock" => 30,  "category" => "Electronics"],
    ["sku" => "C001", "name" => "Cable",        "price" => 7.50,  "stock" => 500, "category" => "Electronics"],
    ["sku" => "D001", "name" => "Doohickey",    "price" => 8.50,  "stock" => 0,   "category" => "Tools"],
    ["sku" => "T001", "name" => "Thingamajig",  "price" => 22.00, "stock" => 75,  "category" => "Toys"],
    ["sku" => "G002", "name" => "Gizmo",        "price" => 31.00, "stock" => 45,  "category" => "Electronics"],
    ["sku" => "W002", "name" => "Whatchamacallit","price"=> 15.75, "stock" => 200, "category" => "Toys"],
];

// 1. Out of stock
$outOfStock = array_filter($inventory, fn($item) => $item["stock"] === 0);
$outNames = array_column($outOfStock, "name");
echo "āŒ Out of stock: " . implode(", ", $outNames) . "\n";

// 2. Total inventory value
$inStock = array_filter($inventory, fn($item) => $item["stock"] > 0);
$totalValue = array_reduce($inStock,
    fn($sum, $item) => $sum + ($item["price"] * $item["stock"]),
    0.0
);
echo "šŸ’° Total inventory value: $" . number_format($totalValue, 2) . "\n";

// 3. Group by category
$grouped = array_reduce($inventory, function($carry, $item) {
    $carry[$item["category"]][] = $item;
    return $carry;
}, []);

echo "\nšŸ“ By Category:\n";
foreach ($grouped as $cat => $items) {
    echo "  $cat: " . count($items) . " items\n";
}

// 4. Most expensive per category
echo "\nšŸ‘‘ Most Expensive per Category:\n";
foreach ($grouped as $cat => $items) {
    usort($items, fn($a, $b) => $b["price"] <=> $a["price"]);
    echo "  $cat: {$items[0]['name']} (\${$items[0]['price']})\n";
}

// 5. Price-sorted list
$sorted = $inventory;
usort($sorted, fn($a, $b) => $a["price"] <=> $b["price"]);
echo "\nšŸ’² Products by Price:\n";
foreach ($sorted as $item) {
    echo sprintf("  \$%6.2f  %-20s [%s]\n",
        $item["price"], $item["name"], $item["category"]);
}

šŸŽÆ Quick Quiz

Question 1: What does array_map() return?

Question 2: What happens to keys when you use array_filter()?

Question 3: Do PHP sort functions return a new array or modify in-place?

Question 4: What does array_column($arr, "email") do?

Question 5: What does the spaceship operator (<=>) return?

Summary

šŸŽ‰ Key Takeaways

  • array_map() — Transform every element: same size array, new values
  • array_filter() — Keep matching elements: smaller array, original values preserved with keys
  • array_reduce() — Collapse to one value: sum, product, string, grouped array
  • Sorting — sort/rsort (indexed), asort/arsort (associative by value), ksort (by key), usort (custom) — all modify in-place
  • Merging — array_merge() or spread; array_combine() zips keys + values; array_unique() deduplicates
  • Slicing — array_slice() extracts portions; array_splice() removes/replaces in-place; array_chunk() splits into groups
  • Searching — array_column() extracts a field; array_keys/values(); array_flip() for fast lookup; array_count_values() for frequency
  • Utilities — implode/explode, range(), compact/extract, array_fill()
  • Pipelines — Chain filter → map → sort → reduce for powerful data processing

šŸ“š Additional Resources

šŸš€ What's Next?

You've conquered arrays — now it's time for their text counterpart. In Lesson 10: Strings & String Functions, you'll master PHP's string manipulation toolkit: strlen, strpos, substr, str_replace, sprintf, regular expressions basics, and more.

šŸŽ‰ Excellent Progress!

You now have a powerful toolkit for processing data in PHP. The combination of arrays + array functions is what you'll use in nearly every real-world project.