← Lekcje
PHP · lekcja 07

Pętle 2/2: foreach i tablice w praktyce

Dla każdego elementu tablicy zrób…

  1. foreach po tablicy zwykłej i asocjacyjnej ($key => $value)
  2. Pętla w pętli po tablicy tablic: lista uczniów jako tabela HTML
  3. Pomocnicy: array_sum, count, in_array, sort
  4. Ćwiczenie: grades.php ze średnią i ostrzeżeniem
Rozgrzewka · 3 minuty

Rozgrzewka: for i while

for ($i = 2; $i <= 10; $i += 4) { echo $i . " "; }
  1. Co wypisze ta pętla i ile razy się wykona?
  2. Kiedy sięgacie po while, a kiedy po for?
  3. Co się stanie, gdy w pętli while zapomnicie $i++?
Część 1 · foreach

foreach: dla każdej szuflady

$games 0 Minecraft 1 Fortnite 2 FIFA szuflada po szufladzie, od 0 do końca 1. powtórzenie: $game = "Minecraft" 2. powtórzenie: $game = "Fortnite" 3. powtórzenie: "FIFA", potem koniec: szuflad już nie ma
$games = ["Minecraft", "Fortnite", "FIFA"];
echo "<ul>";
foreach ($games as $game) {
    echo "<li>$game</li>";
}
echo "</ul>";

Czytacie: „dla każdego elementu z $games, nazwanego $game, zrób…”. Bez licznika, bez warunku, bez count: PHP sam wie, ile jest szuflad, i sam przechodzi do następnej.

PHP Manual: foreach („On each iteration, the value of the current element is assigned to $value”) · php.net/manual/en/control-structures.foreach.php
Część 1 · foreach z kluczem

Tablica asocjacyjna: $key => $value

$student = [
    "name" => "Anna",
    "age"  => 18,
    "city" => "Warszawa",
];
foreach ($student as $key => $value) {
    echo "$key: $value<br>";
}
// name: Anna
// age: 18
// city: Warszawa
foreach ($games as $index => $game) {
    echo ($index + 1) . ". $game<br>";
}
// 1. Minecraft
// 2. Fortnite
// 3. FIFA
"name" "Anna" "age" 18 "city" "Warszawa" $key → ← $value 2. powtórzenie: $key = "age", $value = 18

Dwie zmienne: podpis szuflady (klucz) i zawartość (wartość). Na zwykłej tablicy kluczem jest numer od 0. Nazwy zmiennych wybieracie sami.

Ciekawostka z PHP Manual: od PHP 7.1.0 foreach potrafi rozpakować od razu dwie wartości z zagnieżdżonej tablicy, np. foreach ($pairs as [$a, $b]) zamiast osobno wyciągać $pair[0] i $pair[1]. To ten sam pomysł co $key => $value wyżej, tylko zamiast nazwy klucza dostajecie od razu dwie wartości z jednego wiersza.

PHP Manual: foreach („The second form will additionally assign the current element's key to the $key variable on each iteration”) · php.net/manual/en/control-structures.foreach.php · ciekawostka: PHP Manual, foreach, sekcja o array destructuring („Please note that array destructuring via [] is only possible as of PHP 7.1.0.”) · ta sama strona
Część 2 · Pętla w pętli

Tablica tablic: wiersze i komórki

$students = [
    ["name" => "Anna",   "grades" => [5, 4, 5, 6]],
    ["name" => "Bartek", "grades" => [3, 2, 3]],
];
echo "<table>";
foreach ($students as $student) {          // wiersz
    echo "<tr><td>" . $student["name"] . "</td><td>";
    foreach ($student["grades"] as $grade) {  // oceny
        echo $grade . " ";
    }
    echo "</td></tr>";
}
echo "</table>";
foreach ($students as $student) Anna 5 4 5 6 Bartek 3 2 3 wiersz foreach ($student["grades"] as $grade) zewnętrzna: uczeń za uczniem · wewnętrzna: ocena za oceną

Ten sam schemat co tabliczka mnożenia: zewnętrzna pętla robi wiersz, wewnętrzna wypełnia go. Różnica: tu liczbę powtórzeń wyznacza tablica, nie licznik.

PHP Manual: foreach · php.net/manual/en/control-structures.foreach.php · PHP Manual: Arrays („trees and multidimensional arrays are also possible”) · php.net/manual/en/language.types.array.php
Część 3 · Funkcje do tablic

Czterech pomocników dla $grades = [4, 5, 3, 5]

FunkcjaCo robiPrzykładWynik
array_sum($grades)sumuje wszystkie wartościarray_sum($grades)17
count($grades)liczy elementyarray_sum($grades) / count($grades)4.25
in_array($x, $grades)sprawdza, czy wartość jest w tablicyin_array(6, $grades)false
sort($grades)sortuje rosnąco, zmienia tablicęsort($grades); print_r($grades);3, 4, 5, 5

Średnia do pokazania: round(array_sum($grades) / count($grades), 2). Pętlą z $sum += $grade też się da, array_sum robi to w jednym słowie. sort po tablicy asocjacyjnej gubi podpisy szuflad.

PHP Manual: array_sum („returns the sum of values in an array”) · php.net/manual/en/function.array-sum.php · in_array („Returns true if needle is found in the array, false otherwise”) · function.in-array.php · sort („Sorts array in place by value in ascending order… removing any existing keys”) · function.sort.php · count · function.count.php
Część 4 · Zanim zawołasz o pomoc

Kiedy foreach, kiedy for, i typowe błędy?

SytuacjaCo się dziejePoprawka
Macie tablicę i chcecie przejść po wszystkimfor z count działa, ale łatwo pomylić < z <=foreach: bez licznika nie ma błędu „o jeden”
Chcecie liczby od 1 do 10 albo co drugi elementnie ma tablicy, po której można iśćfor z licznikiem i krokiem
foreach ($games as $game) { $game = "X"; }$games bez zmian: $game to kopia wartościzmieniacie przez klucz: $games[$index] = "X"
foreach po zmiennej, która nie jest tablicąWarning: foreach() argument must be of type array|object, int givenprint_r pokaże, co naprawdę jest w zmiennej
echo $student["grades"];wypisze słowo Array i ostrzeżenie; tablicy nie da się wypisać jednym echowewnętrzny foreach albo implode(", ", …) (łączy elementy tablicy w jeden tekst)
PHP Manual: foreach („foreach will issue an error when used with a variable containing a different data type”; kopia wartości, zmiana przez referencję &$value zostaje na później) · php.net/manual/en/control-structures.foreach.php
Egzamin INF.03

Tak pytają na egzaminie

1. Która pętla w języku PHP pozwala wykonać operacje na wszystkich elementach tablicy z automatycznym indeksowaniem jej elementów?

  1. for
  2. while
  3. foreach
  4. do...while
for($i = 1; $i <= 3; $i++) {
    for($j = 1; $j <= 3; $j++) {
        echo "Iteracja: ($i, $j)";
        if($i == 2 and $j == 2) break;
    }
}

2. Instrukcja break w przedstawionym kodzie PHP sprawi, że

  1. działanie obu pętli zostanie zakończone po iteracji (2, 2).
  2. iteracja (2, 2) zostanie przerwana i nastąpi po niej iteracja (2, 3).
  3. wewnętrzna pętla dokończy działanie dla $j = 3, a następnie program zostanie przerwany.
  4. działanie wewnętrznej pętli zostanie zakończone po iteracji (2, 2) i będzie kontynuowane dla $i = 3.

and to inny zapis &&.

CKE, EE.09, styczeń 2021, zad. 6 · CKE, EE.09, styczeń 2023, zad. 8
Ćwiczenie przy komputerze · 12 minut

Ćwiczenie 1: grades.php

  1. W folderze htdocs utwórz grades.php ze szkieletem HTML. W <head> styl: td, th { border: 1px solid #999; padding: 4px; } oraz .warning { background: #ffd0d0; }.
  2. Tablica $students: 5 uczniów, każdy to tablica z "name" i "grades" (od 3 do 4 ocen, jak na slajdzie). Co najmniej jeden ze średnią poniżej 2.
  3. Tabela HTML z nagłówkiem Imię | Oceny | Średnia. Jeden wiersz na ucznia (foreach), oceny wypisane wewnętrznym foreach, średnia z round(array_sum(...) / count(...), 2).
  4. Średnia poniżej 2: wiersz z class="warning" (operator ?:). Sprawdź w localhost/grades.php.

Ma się pojawić: tabela z 5 wierszami, np. Bartek | 3 2 3 | 2.67, a wiersz ucznia ze średnią 1.5 na różowo. Zero ostrzeżeń „Warning”.

Dla szybkich: pod tabelą wypisz najwyższą średnią w klasie (podpowiedź: zbieraj średnie do tablicy $averages[], potem max($averages)).

Cały plik
$students = [
    ["name" => "Anna",   "grades" => [5, 4, 5, 6]],
    ["name" => "Bartek", "grades" => [3, 2, 3]],
    ["name" => "Celina", "grades" => [1, 2, 1, 2]],
    ["name" => "Dawid",  "grades" => [4, 4, 5]],
    ["name" => "Ewa",    "grades" => [2, 1, 2]],
];
echo "<table><tr><th>Imię</th><th>Oceny</th><th>Średnia</th></tr>";
foreach ($students as $student) {
    $average = round(array_sum($student["grades"]) / count($student["grades"]), 2);
    $rowClass = $average < 2 ? ' class="warning"' : '';
    echo "<tr$rowClass><td>" . $student["name"] . "</td><td>";
    foreach ($student["grades"] as $grade) {
        echo $grade . " ";
    }
    echo "</td><td>$average</td></tr>";
}
echo "</table>";
WierszŚredniaKlasa
Anna5
Bartek2.67
Celina1.5warning
Dawid4.33
Ewa1.67warning

Dla szybkich: $averages[] = $average; w pętli, po pętli echo max($averages); daje 5.

PHP Manual, php.net/manual/en/function.round.php · php.net/manual/en/function.max.php · Zasady pracowni
Ćwiczenie na kartce · 5 minut

Ćwiczenie 2: co wypisze?

Bez komputera. Zapisz wynik każdego fragmentu, potem sprawdź w XAMPP.

// A
foreach ([3, 5, 7] as $n) {
    echo $n * 2 . " ";
}

// B
$prices = ["bread" => 5, "milk" => 4];
foreach ($prices as $product => $price) {
    echo "$product: $price; ";
}
// C
$grades = [4, 5, 3];
echo array_sum($grades) / count($grades);

// D
$names = ["Ola", "Jan", "Adam"];
sort($names);
echo $names[0];

Pułapka w D: sort zmienia tablicę, więc [0] to już nie „Ola”. Kto ma 4 na 4, tłumaczy B sąsiadowi.

Rozwiązanie
FragmentWynikDlaczego?
A6 10 14każdy element razy 2
Bbread: 5; milk: 4;klucz i wartość po kolei
C412 / 3
DAdampo sort alfabetycznie: Adam, Jan, Ola
Na koniec

Sprawdź się

  1. Czym foreach różni się od for i kiedy wybieracie które?
  2. Co daje zapis foreach ($student as $key => $value) w porównaniu z foreach ($student as $value)?
  3. Jak policzycie średnią z tablicy ocen w jednej linii?

Następny temat: funkcje w PHP. Później w tym roku bazy danych: wiersze z MySQL przyjdą jako tablica tablic, dokładnie taka jak $students z dzisiaj.

Poza szkołą

A jak to wygląda w produkcji?

Na lekcjiW produkcjiStatus
$students wpisane ręcznie w kodzieTa sama tablica tablic przychodzi z bazy danych: $students = $result->fetch_all(MYSQLI_ASSOC); i dalej dokładnie ten sam foreach, co dziś. To już połowa tego, co czeka Was przy bazach danych.nadal w użyciu
foreach + echo "<tr>"W szablonach (Blade, Twig) pętla wygląda tak: @foreach ($students as $student) … @endforeach. Ten sam pomysł, inny zapis, HTML czysty.nadal w użyciu
array_sum, count, sortPHP ma kilkadziesiąt funkcji do tablic (array_map, array_filter, usort…). W zespole najpierw szuka się gotowej funkcji, dopiero potem pisze pętlę.nadal w użyciu
for ($i = 0; $i < count($arr); $i++)Tablice dziś przechodzi się przez foreach. Narzędzia sprawdzające kod zgłaszają count liczone w każdym powtórzeniu pętli jako stratę czasu.zastępowane
mysqli_result::fetch_all: PHP Manual · php.net/manual/en/mysqli-result.fetch-all.php · lista funkcji tablicowych: php.net/manual/en/ref.array.php
← → slajdy · F pełny ekran · S spis