Array Functions Lanjutan #
Artikel Array di section Dasar sudah membahas fungsi-fungsi array yang paling sering digunakan. Artikel ini menjadi kelanjutannya — membahas fungsi yang lebih jarang diketahui tapi sangat berguna untuk masalah tertentu: array_multisort untuk sorting multi-kolom, array_walk_recursive untuk transformasi nested array, fungsi array_u* untuk operasi set dengan komparator kustom, serta pola pipeline data yang merangkai beberapa fungsi array menjadi transformasi yang ekspresif dan mudah dibaca.
Sorting Lanjutan #
array_multisort — Sorting Multi-kolom
#
array_multisort memungkinkan sorting array berdasarkan beberapa kriteria sekaligus — seperti ORDER BY kolom1 ASC, kolom2 DESC di SQL:
<?php
$produk = [
['nama' => 'Laptop', 'kategori' => 'elektronik', 'harga' => 15000000, 'stok' => 5],
['nama' => 'Mouse', 'kategori' => 'aksesoris', 'harga' => 250000, 'stok' => 0],
['nama' => 'Monitor', 'kategori' => 'elektronik', 'harga' => 5000000, 'stok' => 12],
['nama' => 'Keyboard', 'kategori' => 'aksesoris', 'harga' => 800000, 'stok' => 8],
['nama' => 'Webcam', 'kategori' => 'elektronik', 'harga' => 1200000, 'stok' => 3],
];
// Ekstrak kolom yang akan di-sort
$kategori = array_column($produk, 'kategori');
$harga = array_column($produk, 'harga');
// Sort: kategori ascending, lalu harga descending dalam kategori yang sama
array_multisort(
$kategori, SORT_ASC, SORT_STRING,
$harga, SORT_DESC, SORT_NUMERIC,
$produk
);
foreach ($produk as $p) {
echo "{$p['kategori']} | {$p['nama']}: Rp " . number_format($p['harga']) . "\n";
}
// aksesoris | Keyboard: Rp 800.000
// aksesoris | Mouse: Rp 250.000
// elektronik | Laptop: Rp 15.000.000
// elektronik | Monitor: Rp 5.000.000
// elektronik | Webcam: Rp 1.200.000
// Flag sorting yang tersedia:
// SORT_REGULAR — perbandingan default PHP
// SORT_NUMERIC — bandingkan sebagai angka
// SORT_STRING — bandingkan sebagai string
// SORT_NATURAL — natural order (file10 setelah file9)
// SORT_LOCALE_STRING — bandingkan menggunakan locale
// SORT_FLAG_CASE — case-insensitive (dikombinasikan dengan SORT_STRING atau SORT_NATURAL)
Sorting dengan usort Multi-kriteria via Tuple
#
Alternatif yang lebih idiomatik di PHP 8:
<?php
// Menggunakan spaceship operator dengan array comparison
usort($produk, fn($a, $b) =>
[$a['kategori'], -$a['harga']] <=> [$b['kategori'], -$b['harga']]
);
// Sort: kategori ascending, harga descending (negatif untuk membalik)
// Multi-kriteria yang lebih kompleks
usort($produk, function($a, $b) {
// 1. Kategori ascending
$cmp = strcmp($a['kategori'], $b['kategori']);
if ($cmp !== 0) return $cmp;
// 2. Stok: tersedia dulu (stok > 0)
$aAda = $a['stok'] > 0 ? 0 : 1;
$bAda = $b['stok'] > 0 ? 0 : 1;
if ($aAda !== $bAda) return $aAda <=> $bAda;
// 3. Harga ascending
return $a['harga'] <=> $b['harga'];
});
array_walk dan array_walk_recursive
#
array_walk mengiterasi array dan memanggil callback untuk setiap elemen, bisa memodifikasi nilai via referensi. array_walk_recursive melakukan hal yang sama tapi masuk ke nested array:
<?php
// array_walk — modifikasi in-place
$harga = ['laptop' => 15000000, 'mouse' => 250000, 'monitor' => 5000000];
array_walk($harga, function(&$nilai, $kunci) {
$nilai = 'Rp ' . number_format($nilai, 0, ',', '.');
});
print_r($harga);
// ['laptop' => 'Rp 15.000.000', 'mouse' => 'Rp 250.000', 'monitor' => 'Rp 5.000.000']
// Dengan data tambahan (argumen ketiga)
$config = ['host' => 'localhost', 'port' => '3306', 'db' => 'myapp'];
$prefix = 'DB_';
array_walk($config, function(&$nilai, $kunci) use ($prefix) {
putenv("$prefix" . strtoupper($kunci) . "=$nilai");
});
// array_walk_recursive — masuk ke nested array
$data = [
'user' => ['nama' => ' Budi ', 'email' => ' [email protected] '],
'tags' => [' php ', ' MYSQL ', ' backend '],
'versi' => ' 2.0 ',
];
array_walk_recursive($data, function(&$nilai) {
if (is_string($nilai)) {
$nilai = strtolower(trim($nilai));
}
});
print_r($data);
// ['user' => ['nama' => 'budi', 'email' => '[email protected]'],
// 'tags' => ['php', 'mysql', 'backend'],
// 'versi' => '2.0']
// Transformasi nested config dari YAML/JSON
$config = [
'database' => [
'host' => '${DB_HOST}',
'password' => '${DB_PASS}',
],
'cache' => [
'ttl' => '${CACHE_TTL}',
],
];
array_walk_recursive($config, function(&$nilai) {
if (is_string($nilai) && preg_match('/^\$\{(.+)\}$/', $nilai, $m)) {
$nilai = getenv($m[1]) ?: $nilai;
}
});
Operasi Set dengan Komparator Kustom #
PHP menyediakan versi u (user-defined) dari fungsi diff dan intersect — memungkinkan komparator kustom untuk perbandingan yang lebih canggih:
<?php
$produkA = [
['id' => 1, 'nama' => 'Laptop', 'harga' => 15000000],
['id' => 2, 'nama' => 'Mouse', 'harga' => 250000],
['id' => 3, 'nama' => 'Monitor', 'harga' => 5000000],
];
$produkB = [
['id' => 2, 'nama' => 'Mouse Gaming', 'harga' => 350000], // ID sama, nama berbeda
['id' => 4, 'nama' => 'Keyboard', 'harga' => 800000],
];
// array_udiff — elemen di A yang tidak ada di B (pakai komparator kustom)
$hanyaDiA = array_udiff($produkA, $produkB, fn($a, $b) => $a['id'] <=> $b['id']);
// Produk dengan ID yang tidak ada di $produkB: Laptop (id:1) dan Monitor (id:3)
// array_uintersect — elemen yang ada di keduanya (berdasarkan ID)
$sama = array_uintersect($produkA, $produkB, fn($a, $b) => $a['id'] <=> $b['id']);
// Mouse (id:2) — ada di keduanya
// array_diff_uassoc — diff berdasarkan key dan nilai (komparator kustom untuk nilai)
$configDefault = ['timeout' => 30, 'retry' => 3, 'debug' => false];
$configUser = ['timeout' => 60, 'retry' => 3, 'log' => true];
$berbeda = array_diff_uassoc($configDefault, $configUser, fn($a, $b) => $a <=> $b);
// ['timeout' => 30] — key 'timeout' ada di keduanya tapi nilainya berbeda
// array_udiff_assoc — diff nilai dengan komparator kustom, key harus sama
$produkLama = ['laptop' => 15000000, 'mouse' => 250000];
$produkBaru = ['laptop' => 14000000, 'mouse' => 250000, 'webcam' => 500000];
$hargaBerubah = array_udiff_assoc(
$produkLama,
$produkBaru,
fn($a, $b) => $a <=> $b
);
// ['laptop' => 15000000] — harga laptop berubah
array_fill_keys dan array_fill
#
<?php
// array_fill_keys — buat array dari daftar key dengan nilai default yang sama
$fields = ['nama', 'email', 'telepon', 'alamat'];
$formKosong = array_fill_keys($fields, '');
// ['nama' => '', 'email' => '', 'telepon' => '', 'alamat' => '']
// Berguna untuk inisialisasi counter
$kategori = ['elektronik', 'aksesoris', 'furniture'];
$counter = array_fill_keys($kategori, 0);
// ['elektronik' => 0, 'aksesoris' => 0, 'furniture' => 0]
foreach ($produk as $p) {
$counter[$p['kategori']]++;
}
// Merge dengan data yang ada — isi default untuk key yang tidak ada
$dataUser = ['nama' => 'Budi', 'email' => '[email protected]'];
$dataLengkap = array_merge($formKosong, $dataUser);
// ['nama' => 'Budi', 'email' => '[email protected]', 'telepon' => '', 'alamat' => '']
// array_fill — buat array dengan nilai yang sama sebanyak N
$defaultPermisi = array_fill(0, 5, false); // [false, false, false, false, false]
$slot = array_fill(1, 7, null); // {1: null, 2: null, ..., 7: null}
// Kombinasi untuk buat jadwal mingguan
$hari = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Minggu'];
$jadwal = array_fill_keys($hari, []);
// ['Senin' => [], 'Selasa' => [], ..., 'Minggu' => []]
array_splice — Operasi Penuh
#
array_splice adalah fungsi serbaguna yang bisa menghapus, mengganti, dan menyisipkan sekaligus:
<?php
$buah = ['apel', 'mangga', 'jeruk', 'anggur', 'semangka'];
// Hapus 2 elemen mulai indeks 1
$dihapus = array_splice($buah, 1, 2);
// $buah: ['apel', 'anggur', 'semangka']
// $dihapus: ['mangga', 'jeruk']
// Sisipkan tanpa menghapus (panjang = 0)
array_splice($buah, 2, 0, ['kiwi', 'lemon']);
// $buah: ['apel', 'anggur', 'kiwi', 'lemon', 'semangka']
// Ganti 1 elemen dengan 2 elemen baru
array_splice($buah, 1, 1, ['mangga harum', 'mangga arum']);
// $buah: ['apel', 'mangga harum', 'mangga arum', 'kiwi', 'lemon', 'semangka']
// Hapus dari belakang (indeks negatif)
array_splice($buah, -2); // hapus 2 elemen terakhir
// Perbandingan: array_splice vs unset
$arr = ['a', 'b', 'c', 'd'];
unset($arr[1]);
// ['a', 'c', 'd'] — indeks berlubang: [0]=>'a', [2]=>'c', [3]=>'d'
$arr = ['a', 'b', 'c', 'd'];
array_splice($arr, 1, 1);
// ['a', 'c', 'd'] — indeks di-reindex: [0]=>'a', [1]=>'c', [2]=>'d'
array_pad — Tambah Elemen ke Ukuran Tertentu
#
<?php
$data = [1, 2, 3];
// Pad ke kanan (positif) — tambah di akhir
$padded = array_pad($data, 5, 0); // [1, 2, 3, 0, 0]
$padded = array_pad($data, 5, null); // [1, 2, 3, null, null]
// Pad ke kiri (negatif) — tambah di awal
$padded = array_pad($data, -5, 0); // [0, 0, 1, 2, 3]
// Tidak melakukan apa-apa jika array sudah cukup panjang
$padded = array_pad($data, 2, 0); // [1, 2, 3] — tidak berubah
// Berguna untuk memastikan array punya ukuran minimum
function pastikanMinimum(array $arr, int $min, mixed $default = null): array
{
return count($arr) >= $min ? $arr : array_pad($arr, $min, $default);
}
// Contoh: tampilkan 5 produk terbaru, isi placeholder jika kurang
$produkTerbaru = ambilProdukTerbaru(limit: 3); // hanya 3 produk
$tampilan = pastikanMinimum($produkTerbaru, 5, ['placeholder' => true]);
// Sekarang punya 5 elemen, 2 terakhir adalah placeholder
compact dan extract
#
<?php
// compact() — buat array asosiatif dari variabel yang ada
$nama = 'Budi Santoso';
$email = '[email protected]';
$umur = 28;
$role = 'admin';
$user = compact('nama', 'email', 'umur', 'role');
// ['nama' => 'Budi Santoso', 'email' => '[email protected]', 'umur' => 28, 'role' => 'admin']
// Berguna untuk build data array sebelum simpan ke DB atau kirim ke view
$stmt = $pdo->prepare("INSERT INTO users (nama, email, umur, role) VALUES (:nama, :email, :umur, :role)");
$stmt->execute(compact('nama', 'email', 'umur', 'role'));
// extract() — kebalikan compact: buat variabel dari key array
$config = ['host' => 'localhost', 'port' => 3306, 'nama' => 'mydb'];
extract($config);
// Sekarang ada variabel $host, $port, $nama di scope ini
echo $host; // localhost
echo $port; // 3306
// PERHATIAN: extract dari data user SANGAT BERBAHAYA!
// Jangan pernah: extract($_POST) atau extract($_GET)
// Attacker bisa menginject variabel sembarangan ke scope
// extract aman dengan prefix
extract($config, EXTR_PREFIX_ALL, 'db');
// $db_host, $db_port, $db_nama
// Atau dengan EXTR_SKIP — skip jika variabel sudah ada
$host = 'nilai_yang_sudah_ada'; // tidak akan di-overwrite
extract($config, EXTR_SKIP);
echo $host; // masih 'nilai_yang_sudah_ada'
Pola Pipeline Data #
Merangkai fungsi array menjadi transformasi data yang expresif:
<?php
$orders = [
['id' => 1, 'status' => 'selesai', 'total' => 150000, 'user_id' => 10, 'diskon' => 0],
['id' => 2, 'status' => 'batal', 'total' => 80000, 'user_id' => 12, 'diskon' => 0.1],
['id' => 3, 'status' => 'selesai', 'total' => 2500000, 'user_id' => 10, 'diskon' => 0.15],
['id' => 4, 'status' => 'selesai', 'total' => 300000, 'user_id' => 15, 'diskon' => 0],
['id' => 5, 'status' => 'proses', 'total' => 900000, 'user_id' => 12, 'diskon' => 0.05],
];
// Pipeline: filter selesai → hitung net → group by user → hitung total per user
$pendapatanPerUser = array_reduce(
array_map(
fn($o) => ['user_id' => $o['user_id'], 'net' => $o['total'] * (1 - $o['diskon'])],
array_filter($orders, fn($o) => $o['status'] === 'selesai')
),
function($carry, $item) {
$carry[$item['user_id']] = ($carry[$item['user_id']] ?? 0) + $item['net'];
return $carry;
},
[]
);
// [10 => 2277500, 15 => 300000]
// Buat kelas pipeline untuk keterbacaan yang lebih baik
class ArrayPipeline
{
private function __construct(private array $data) {}
public static function from(array $data): static
{
return new static($data);
}
public function filter(callable $fn): static
{
return new static(array_values(array_filter($this->data, $fn)));
}
public function map(callable $fn): static
{
return new static(array_map($fn, $this->data));
}
public function sort(callable $fn): static
{
$data = $this->data;
usort($data, $fn);
return new static($data);
}
public function groupBy(string $key): array
{
return array_reduce($this->data, function($carry, $item) use ($key) {
$carry[$item[$key]][] = $item;
return $carry;
}, []);
}
public function reduce(callable $fn, mixed $initial = null): mixed
{
return array_reduce($this->data, $fn, $initial);
}
public function toArray(): array
{
return $this->data;
}
public function first(): mixed
{
return $this->data[0] ?? null;
}
public function count(): int
{
return count($this->data);
}
}
// Penggunaan fluent pipeline
$hasil = ArrayPipeline::from($orders)
->filter(fn($o) => $o['status'] === 'selesai')
->map(fn($o) => [...$o, 'net' => $o['total'] * (1 - $o['diskon'])])
->sort(fn($a, $b) => $b['net'] <=> $a['net'])
->toArray();
$totalPendapatan = ArrayPipeline::from($orders)
->filter(fn($o) => $o['status'] === 'selesai')
->reduce(fn($carry, $o) => $carry + $o['total'] * (1 - $o['diskon']), 0);
$perStatus = ArrayPipeline::from($orders)->groupBy('status');
// ['selesai' => [...], 'batal' => [...], 'proses' => [...]]
Fungsi Array Lainnya yang Jarang Diketahui #
<?php
// array_count_values — hitung frekuensi setiap nilai
$nilai = ['apel', 'mangga', 'apel', 'jeruk', 'mangga', 'apel'];
$freq = array_count_values($nilai);
// ['apel' => 3, 'mangga' => 2, 'jeruk' => 1]
// Berguna untuk tag cloud, analisis data
$tags = ['php', 'mysql', 'php', 'redis', 'php', 'mysql'];
$popular = array_count_values($tags);
arsort($popular); // urutkan dari terpopuler
// ['php' => 3, 'mysql' => 2, 'redis' => 1]
// array_flip — tukar key dan value
$buah = ['apel' => 'red', 'mangga' => 'yellow', 'anggur' => 'purple'];
$flip = array_flip($buah);
// ['red' => 'apel', 'yellow' => 'mangga', 'purple' => 'anggur']
// Berguna untuk lookup cepat
$statusCodes = [200 => 'OK', 404 => 'Not Found', 500 => 'Error'];
$codeLookup = array_flip($statusCodes);
echo $codeLookup['Not Found']; // 404
// array_combine — gabungkan dua array jadi key => value
$kolom = ['id', 'nama', 'email'];
$nilai = [1, 'Budi', '[email protected]'];
$row = array_combine($kolom, $nilai);
// ['id' => 1, 'nama' => 'Budi', 'email' => '[email protected]']
// Berguna saat parse CSV dengan header
$handle = fopen('data.csv', 'r');
$header = fgetcsv($handle);
while (($row = fgetcsv($handle)) !== false) {
$data[] = array_combine($header, $row);
}
// array_zip_key (tidak ada bawaan, tapi bisa dibuat)
function arrayZip(array ...$arrays): array
{
return array_map(null, ...$arrays);
}
$nama = ['Budi', 'Siti', 'Dani'];
$nilai = [85, 92, 78];
$zip = arrayZip($nama, $nilai);
// [['Budi', 85], ['Siti', 92], ['Dani', 78]]
// range() dengan array
$genap = range(2, 20, 2); // [2,4,6,...,20]
$huruf = range('a', 'z'); // ['a','b',...,'z']
$mundur = range(10, 1, -1); // [10,9,8,...,1]
// list() / destrukturing dalam loop
$koordinat = [[1, 2], [3, 4], [5, 6]];
foreach ($koordinat as [$x, $y]) {
echo "($x, $y)\n";
}
// array_map dengan null — transpose matriks
$matriks = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
$transpose = array_map(null, ...$matriks);
// [[1,4,7], [2,5,8], [3,6,9]]
Ringkasan #
array_multisortuntuk sorting multi-kolom dengan flag tipe (SORT_STRING,SORT_NUMERIC,SORT_NATURAL) — alternatifnya gunakanusortdengan array comparison via spaceship operator untuk kode yang lebih mudah dibaca.array_walk_recursiveuntuk transformasi nested array — sangat berguna untuk normalisasi konfigurasi hierarkis, interpolasi env variable, atau sanitasi data multi-level.- Fungsi
array_u*(array_udiff,array_uintersect,array_udiff_assoc) menerima komparator kustom — gunakan untuk operasi set pada array of objects/associative arrays berdasarkan field tertentu.array_fill_keysuntuk inisialisasi array dengan nilai default — lebih bersih dari loop untuk menyiapkan counter, form kosong, atau template data.array_splicevsunset—spliceme-reindex array (indeks kontinu),unsetmembiarkan lubang di indeks. Pilihsplicejika indeks numerik yang kontinu penting.compactuntuk build array dari variabel — singkat dan aman. Hindariextractdari data user karena bisa meng-inject variabel ke scope secara tidak aman.- Pipeline pattern membuat rangkaian transformasi array lebih mudah dibaca — bungkus dalam kelas dengan method chaining untuk code yang ekspresif dan testable.
array_count_valuesuntuk analisis frekuensi — hitung kemunculan setiap nilai dalam satu pemanggilan, berguna untuk tag cloud, analisis top-N, dan statistik sederhana.