JavaScript Next Gen

Modern Array Methods

Menguasai array methods modern dan higher-order functions di JavaScript ES6+

Higher-Order Functions

Higher-order functions adalah fungsi yang menerima fungsi sebagai argument atau mengembalikan fungsi. Array methods modern di JavaScript sebagian besar adalah higher-order functions.

Keuntungan Array Methods Modern

  • Declarative: Menjelaskan apa yang ingin dilakukan, bukan bagaimana
  • Immutable: Tidak mengubah array asli
  • Chainable: Dapat digabungkan untuk operasi kompleks
  • Readable: Kode lebih mudah dibaca dan dipahami

Map, Filter, dan Find

map() - Mengubah Elemen

map() membuat array baru dengan hasil transformasi setiap elemen.

const numbers = [1, 2, 3, 4, 5];

// Ubah setiap elemen
const doubled = numbers.map(num => num * 2);
// [2, 4, 6, 8, 10]

// Dengan objek
const users = [
  { name: "John", age: 25 },
  { name: "Jane", age: 30 }
];

const names = users.map(user => user.name);
// ["John", "Jane"]

filter() - Memilih Elemen

filter() membuat array baru dengan elemen yang memenuhi kondisi.

const numbers = [1, 2, 3, 4, 5, 6];

// Filter bilangan genap
const evens = numbers.filter(num => num % 2 === 0);
// [2, 4, 6]

// Filter user aktif
const activeUsers = users.filter(user => user.active);

find() - Mencari Elemen Tunggal

find() mengembalikan elemen pertama yang memenuhi kondisi.

const users = [
  { id: 1, name: "John" },
  { id: 2, name: "Jane" }
];

const user = users.find(u => u.id === 2);
// { id: 2, name: "Jane" }

Reduce - Pisau Swiss Army

reduce() adalah method paling powerful yang dapat digunakan untuk berbagai operasi agregasi.

const numbers = [1, 2, 3, 4, 5];

const sum = numbers.reduce((total, num) => total + num, 0);
// 15

const product = numbers.reduce((prod, num) => prod * num, 1);
// 120
const numbers = [5, 2, 8, 1, 9];

const max = numbers.reduce((max, num) => 
  num > max ? num : max, numbers[0]);
// 9

const min = numbers.reduce((min, num) => 
  num < min ? num : min, numbers[0]);
// 1
const users = [
  { name: "John", role: "admin" },
  { name: "Jane", role: "user" },
  { name: "Bob", role: "admin" }
];

const byRole = users.reduce((acc, user) => {
  const role = user.role;
  acc[role] = acc[role] || [];
  acc[role].push(user);
  return acc;
}, {});
// { admin: [...], user: [...] }
const nested = [[1, 2], [3, 4], [5]];

const flat = nested.reduce((acc, arr) => 
  acc.concat(arr), []);
// [1, 2, 3, 4, 5]

Some dan Every

some() - Tes Jika Ada yang Lulus

const numbers = [1, 2, 3, 4, 5];

const hasEven = numbers.some(num => num % 2 === 0);
// true

const hasNegative = numbers.some(num => num < 0);
// false

every() - Tes Jika Semua Lulus

const numbers = [2, 4, 6, 8];

const allEven = numbers.every(num => num % 2 === 0);
// true

const allPositive = numbers.every(num => num > 0);
// true

Rantai Method (Method Chaining)

Gabungkan beberapa array methods untuk operasi kompleks:

const users = [
  { name: "John", age: 25, active: true },
  { name: "Jane", age: 30, active: false },
  { name: "Bob", age: 22, active: true },
  { name: "Alice", age: 28, active: true }
];

// Dapatkan nama user aktif di atas 25 tahun
const result = users
  .filter(user => user.active)
  .filter(user => user.age > 25)
  .map(user => user.name);
// ["John", "Alice"]

// Hitung total umur user aktif
const totalAge = users
  .filter(user => user.active)
  .reduce((sum, user) => sum + user.age, 0);
// 75

Tips Performa

Method chaining membuat kode readable, tetapi setiap method membuat iterasi baru. Untuk array besar, pertimbangkan menggunakan single reduce() atau loop tradisional untuk performa optimal.

Implementasi Array Methods

Tambahkan ke js/app.js:

js/app.js
// ----------------------------
// Modern Array Methods dan Higher-Order Functions
// ----------------------------
export function demoArrayMethods() {
  // Data contoh
  const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  const users = [
    { id: 1, name: "John", age: 25, active: true },
    { id: 2, name: "Jane", age: 30, active: false },
    { id: 3, name: "Bob", age: 22, active: true },
    { id: 4, name: "Alice", age: 28, active: true },
    { id: 5, name: "Charlie", age: 35, active: false }
  ];

  // map
  const doubled = numbers.map(num => num * 2);

  // filter
  const evenNumbers = numbers.filter(num => num % 2 === 0);
  const activeUsers = users.filter(user => user.active);

  // find
  const userJane = users.find(user => user.name === "Jane");

  // some dan every
  const hasAdult = users.some(user => user.age >= 18);
  const allAdults = users.every(user => user.age >= 18);

  // reduce
  const sum = numbers.reduce((total, num) => total + num, 0);
  const oldest = users.reduce((oldest, user) => 
    user.age > oldest.age ? user : oldest, users[0]);

  // Menggabungkan methods (chaining)
  const activeUsersNames = users
    .filter(user => user.active)
    .map(user => user.name);

  const totalActiveAge = users
    .filter(user => user.active)
    .reduce((sum, user) => sum + user.age, 0);

  return {
    map: doubled,
    filter: {
      evenNumbers,
      activeUsers: activeUsers.length
    },
    find: userJane,
    some: hasAdult,
    every: allAdults,
    reduce: {
      sum,
      oldest: oldest.name
    },
    chaining: {
      activeUsersNames,
      totalActiveAge
    }
  };
}

Fitur Array Lanjutan

Array.from()

Membuat array dari iterable atau array-like objects:

// Dari string
Array.from("hello"); // ["h", "e", "l", "l", "o"]

// Dengan fungsi map
Array.from([1, 2, 3], x => x * 2); // [2, 4, 6]

// Dari NodeList
const divs = Array.from(document.querySelectorAll('div'));

Array.of()

Membuat array dari arguments:

Array.of(1, 2, 3); // [1, 2, 3]
Array.of("hello"); // ["hello"]

flat() dan flatMap()

// Meratakan array bersarang
const nested = [1, [2, 3], [[4, 5], 6]];
nested.flat();    // [1, 2, 3, [4, 5], 6]
nested.flat(2);   // [1, 2, 3, 4, 5, 6]

// Map dan ratakan dalam satu langkah
const numbers = [1, 2, 3];
numbers.flatMap(n => [n, n * 2]); // [1, 2, 2, 4, 3, 6]

includes()

Cek apakah array mengandung nilai:

const numbers = [1, 2, 3, 4, 5];
numbers.includes(3);    // true
numbers.includes(10);   // false
numbers.includes(3, 3); // false (mulai dari index 3)

Implementasi Advanced Arrays

Tambahkan ke js/app.js:

js/app.js
// ----------------------------
// Array Destructuring dan Spread Lanjutan
// ----------------------------
export function demoAdvancedArrays() {
  const numbers = [1, 2, 3, 4, 5];

  // Clone dan concat
  const numbersCopy = [...numbers];
  const moreNumbers = [...numbers, 6, 7, 8];
  const combinedArrays = [...numbers, ...[6, 7, 8, 9]];

  // Array.from
  const fromIterable = Array.from("hello");
  const withMapFn = Array.from(numbers, n => n * n);

  // Array.of
  const newArray = Array.of(1, "two", { three: 3 });

  // Flat dan FlatMap
  const nestedArrays = [1, [2, 3], [[4, 5], 6]];
  const flattened = nestedArrays.flat();
  const deepFlattened = nestedArrays.flat(2);

  const flatMapped = numbers.flatMap(n => [n, n * 2]);

  // Includes
  const hasThree = numbers.includes(3);

  return {
    clone: numbersCopy,
    concat: {
      moreNumbers,
      combinedArrays
    },
    arrayFrom: {
      fromIterable,
      withMapFn
    },
    arrayOf: newArray,
    flatAndFlatMap: {
      flattened,
      deepFlattened,
      flatMapped
    },
    includes: hasThree
  };
}

Update main.js

Update js/main.js untuk menjalankan demo baru:

js/main.js
// Update import
import { 
  demoVariables, 
  demoArrowFunctions, 
  demoTemplateLiterals, 
  demoDestructuring, 
  demoSpreadRest,
  demoDefaultParams,
  demoClasses,
  demoObjectLiterals,
  demoArrayMethods,
  demoAdvancedArrays
} from './app.js';

// Tambahkan ke fungsi runAllDemos
function runAllDemos() {
  // ... demo sebelumnya ...

  // Demo Array Methods
  const arrayMethodsResults = demoArrayMethods();
  addOutput(
    "9. Modern Array Methods",
    "Higher-Order Functions pada array",
    `map: ${JSON.stringify(arrayMethodsResults.map)}
filter (evenNumbers): ${JSON.stringify(arrayMethodsResults.filter.evenNumbers)}
filter (activeUsers): ${arrayMethodsResults.filter.activeUsers}
find: ${JSON.stringify(arrayMethodsResults.find)}
some (hasAdult): ${arrayMethodsResults.some}
every (allAdults): ${arrayMethodsResults.every}
reduce (sum): ${arrayMethodsResults.reduce.sum}
reduce (oldest): ${arrayMethodsResults.reduce.oldest}
chaining (activeUsersNames): ${JSON.stringify(arrayMethodsResults.chaining.activeUsersNames)}
chaining (totalActiveAge): ${arrayMethodsResults.chaining.totalActiveAge}`
  );

  // Demo Advanced Arrays
  const advArraysResults = demoAdvancedArrays();
  addOutput(
    "10. Advanced Arrays",
    "Fitur array lanjutan",
    `Clone: ${JSON.stringify(advArraysResults.clone)}
Concat: ${JSON.stringify(advArraysResults.concat.combinedArrays)}
Array.from: ${JSON.stringify(advArraysResults.arrayFrom.withMapFn)}
Array.of: ${JSON.stringify(advArraysResults.arrayOf)}
flat: ${JSON.stringify(advArraysResults.flatAndFlatMap.flattened)}
deepFlat: ${JSON.stringify(advArraysResults.flatAndFlatMap.deepFlattened)}
flatMap: ${JSON.stringify(advArraysResults.flatAndFlatMap.flatMapped)}
includes: ${advArraysResults.includes}`
  );
}

Tabel Perbandingan

MethodMengembalikanMengubah AsliKasus Penggunaan
map()Array baruTidakMengubah elemen
filter()Array baruTidakMemilih elemen
reduce()Nilai tunggalTidakAgregasi data
find()Element atau undefinedTidakMencari item tunggal
some()BooleanTidakTes jika ada yang lulus
every()BooleanTidakTes jika semua lulus
forEach()undefinedTidakEfek samping
flat()Array baruTidakMeratakan array
flatMap()Array baruTidakMap + ratakan

Praktik Terbaik

1. Pilih Method yang Tepat

// Baik - maksud jelas
const adults = users.filter(user => user.age >= 18);

// Hindari - tidak jelas
const adults = [];
users.forEach(user => {
  if (user.age >= 18) adults.push(user);
});

2. Gunakan Chaining dengan Bijak

// Baik - rantai yang mudah dibaca
const result = users
  .filter(user => user.active)
  .map(user => user.name)
  .sort();

// Pertimbangkan refactor rantai yang panjang
const activeUsers = users.filter(user => user.active);
const names = activeUsers.map(user => user.name);
const sorted = names.sort();

3. Hindari Efek Samping dalam Methods

// Buruk - efek samping
let total = 0;
numbers.map(n => {
  total += n; // Efek samping!
  return n * 2;
});

// Baik - fungsi murni
const doubled = numbers.map(n => n * 2);
const total = numbers.reduce((sum, n) => sum + n, 0);

Kapan Menggunakan forEach

Gunakan forEach() hanya ketika Anda perlu efek samping (manipulasi DOM, logging, dll). Untuk transformasi data, gunakan map(), filter(), atau reduce().

Latihan

  1. Implementasi fungsi groupBy menggunakan reduce()
  2. Buat fungsi untuk menghitung rata-rata dari array of objects
  3. Transform struktur array bersarang menjadi struktur flat
  4. Gabungkan filter, map, dan reduce untuk analisis data kompleks