Array Functions Lanjutan

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_multisort untuk sorting multi-kolom dengan flag tipe (SORT_STRING, SORT_NUMERIC, SORT_NATURAL) — alternatifnya gunakan usort dengan array comparison via spaceship operator untuk kode yang lebih mudah dibaca.
  • array_walk_recursive untuk 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_keys untuk inisialisasi array dengan nilai default — lebih bersih dari loop untuk menyiapkan counter, form kosong, atau template data.
  • array_splice vs unsetsplice me-reindex array (indeks kontinu), unset membiarkan lubang di indeks. Pilih splice jika indeks numerik yang kontinu penting.
  • compact untuk build array dari variabel — singkat dan aman. Hindari extract dari 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_values untuk analisis frekuensi — hitung kemunculan setiap nilai dalam satu pemanggilan, berguna untuk tag cloud, analisis top-N, dan statistik sederhana.

← Sebelumnya: Filter & Validation   Berikutnya: XML →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact