Skip to main content
JavaScript 5 mins read Devs2.org

JavaScript Array Methods: map, filter, reduce và toàn bộ phương thức mảng bạn cần biết

Khám phá tất tần tật về JavaScript Array Methods — từ map, filter, reduce đến find, some, every, sort. Học cách thao tác mảng chuyên nghiệp với ví dụ thực tế.

#JavaScript #Array Methods #map #filter #reduce #Web Development #Frontend

Minh họa JavaScript Array Methods — map, filter, reduce

Bạn đã bao giờ viết một vòng lặp for dài ngoằng chỉ để duyệt qua một mảng và biến đổi dữ liệu? Nếu bạn vẫn còn đang vật lộn với for (let i = 0; i < arr.length; i++) thì đã đến lúc khám phá sức mạnh của JavaScript Array Methods.

Array methods là những công cụ có sẵn trong JavaScript giúp bạn thao tác với mảng một cách ngắn gọn, dễ đọc và ít lỗi hơn. Trong bài viết này, chúng ta sẽ đi từ cơ bản đến nâng cao, khám phá tất cả các phương thức mảng quan trọng mà mọi web developer cần nắm vững.

Array Methods là gì? Tại sao cần học?

Array methods là các hàm có sẵn (built-in functions) được gắn với prototype của Array trong JavaScript. Chúng cho phép bạn thực hiện các thao tác phổ biến trên mảng mà không cần viết vòng lặp thủ công.

Lợi ích khi dùng array methods:

  • Code ngắn gọn hơn — Giảm boilerplate code so với vòng lặp for
  • Dễ đọc hơn — Tên phương thức mô tả rõ hành động (filter = lọc, map = ánh xạ, find = tìm kiếm)
  • Ít lỗi hơn — Không phải lo lắng về index, điều kiện dừng lặp
  • Có thể chain (nối) được — Kết hợp nhiều thao tác trong một dòng code
  • Tư duy functional — Giúp bạn viết code theo phong cách functional programming

1. forEach() — Duyệt mảng cơ bản

forEach() là phương thức cơ bản nhất — nó thực thi một hàm callback cho mỗi phần tử trong mảng.

const fruits = ['🍎', '🍌', '🍊', '🍇'];

fruits.forEach((fruit, index) => {
  console.log(`Phần tử thứ ${index + 1}: ${fruit}`);
});
// Output:
// Phần tử thứ 1: 🍎
// Phần tử thứ 2: 🍌
// Phần tử thứ 3: 🍊
// Phần tử thứ 4: 🍇

Lưu ý: forEach() không trả về giá trị (undefined). Nếu bạn muốn tạo mảng mới từ mảng hiện tại, hãy dùng map().

// ❌ Sai: forEach không trả về mảng mới
const doubled = [1, 2, 3].forEach(n => n * 2);
console.log(doubled); // undefined

// ✅ Đúng: dùng map
const doubled = [1, 2, 3].map(n => n * 2);
console.log(doubled); // [2, 4, 6]

2. map() — Biến đổi mảng

map() là một trong những phương thức được dùng nhiều nhất. Nó tạo một mảng mới với kết quả của việc áp dụng hàm callback lên mỗi phần tử.

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

// Nhân đôi mỗi số
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// Chuyển đổi mảng số thành chuỗi định dạng
const formatted = numbers.map(num => `Số ${num}`);
console.log(formatted); // ['Số 1', 'Số 2', 'Số 3', 'Số 4', 'Số 5']

Ví dụ thực tế — Render danh sách sản phẩm:

const products = [
  { id: 1, name: 'Laptop', price: 15000000 },
  { id: 2, name: 'Phone', price: 8000000 },
  { id: 3, name: 'Tablet', price: 10000000 },
];

// Tạo mảng các thẻ HTML (thường dùng trong React)
const productCards = products.map(product => `
  <div class="product-card">
    <h3>${product.name}</h3>
    <p>${product.price.toLocaleString('vi-VN')}₫</p>
  </div>
`);

console.log(productCards);
// [
//   '<div class="product-card"><h3>Laptop</h3><p>15.000.000₫</p></div>',
//   '<div class="product-card"><h3>Phone</h3><p>8.000.000₫</p></div>',
//   '<div class="product-card"><h3>Tablet</h3><p>10.000.000₫</p></div>'
// ]

3. filter() — Lọc mảng

filter() tạo một mảng mới chỉ chứa các phần tử thoả mãn điều kiện trong hàm callback.

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Lấy số chẵn
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6, 8, 10]

// Lấy số lớn hơn 5
const bigNumbers = numbers.filter(num => num > 5);
console.log(bigNumbers); // [6, 7, 8, 9, 10]

Ví dụ thực tế — Lọc sản phẩm theo điều kiện:

const users = [
  { name: 'An', age: 17, active: true },
  { name: 'Bình', age: 22, active: false },
  { name: 'Châu', age: 19, active: true },
  { name: 'Dũng', age: 15, active: true },
];

// Lọc người dùng >= 18 tuổi và đang hoạt động
const eligibleUsers = users.filter(user => user.age >= 18 && user.active);
console.log(eligibleUsers);
// [{ name: 'Châu', age: 19, active: true }]

// Lọc bỏ phần tử null/undefined
const data = [1, null, 2, undefined, 3, 0, '', 4];
const cleanData = data.filter(item => item != null && item !== '');
console.log(cleanData); // [1, 2, 3, 0, 4]
// Lưu ý: 0 là falsy, nếu dùng Boolean sẽ mất số 0

4. reduce() — Gom mảng thành một giá trị

reduce() là phương thức mạnh mẽ nhất trong các array methods. Nó thực thi hàm callback trên mỗi phần tử, tích luỹ (accumulate) kết quả thành một giá trị duy nhất.

array.reduce((accumulator, currentValue, index, array) => {
  // accumulator: giá trị tích luỹ từ các lần lặp trước
  // currentValue: giá trị phần tử hiện tại
  // Trả về giá trị mới cho accumulator
}, initialValue);

Ví dụ cơ bản — Tính tổng:

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

const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 15

// Giải thích từng bước:
// acc = 0, curr = 1 → acc = 1
// acc = 1, curr = 2 → acc = 3
// acc = 3, curr = 3 → acc = 6
// acc = 6, curr = 4 → acc = 10
// acc = 10, curr = 5 → acc = 15

Ví dụ thực tế — Gom nhóm dữ liệu:

const orders = [
  { id: 1, customer: 'An', amount: 500000, status: 'completed' },
  { id: 2, customer: 'Bình', amount: 300000, status: 'pending' },
  { id: 3, customer: 'An', amount: 200000, status: 'completed' },
  { id: 4, customer: 'Châu', amount: 450000, status: 'pending' },
  { id: 5, customer: 'Bình', amount: 150000, status: 'completed' },
];

// Tính tổng doanh thu
const totalRevenue = orders
  .filter(order => order.status === 'completed')
  .reduce((sum, order) => sum + order.amount, 0);
console.log(`Tổng doanh thu: ${totalRevenue.toLocaleString('vi-VN')}₫`);
// Tổng doanh thu: 850.000₫

// Gom nhóm đơn hàng theo khách hàng
const groupedByCustomer = orders.reduce((result, order) => {
  if (!result[order.customer]) {
    result[order.customer] = [];
  }
  result[order.customer].push(order);
  return result;
}, {});

console.log(groupedByCustomer);
// {
//   An: [{ id: 1, ... }, { id: 3, ... }],
//   Bình: [{ id: 2, ... }, { id: 5, ... }],
//   Châu: [{ id: 4, ... }]
// }

// Làm phẳng mảng 2 chiều (flat)
const nested = [[1, 2], [3, 4], [5, 6]];
const flattened = nested.reduce((acc, curr) => [...acc, ...curr], []);
console.log(flattened); // [1, 2, 3, 4, 5, 6]

5. find() — Tìm một phần tử

find() trả về phần tử đầu tiên thoả mãn điều kiện, hoặc undefined nếu không tìm thấy.

const users = [
  { id: 1, name: 'An', email: 'an@example.com' },
  { id: 2, name: 'Bình', email: 'binh@example.com' },
  { id: 3, name: 'Châu', email: 'chau@example.com' },
];

// Tìm user theo ID
const user = users.find(u => u.id === 2);
console.log(user); // { id: 2, name: 'Bình', email: 'binh@example.com' }

// Tìm user theo email
const found = users.find(u => u.email === 'chau@example.com');
console.log(found); // { id: 3, name: 'Châu', email: 'chau@example.com' }

// Không tìm thấy → undefined
const notFound = users.find(u => u.id === 99);
console.log(notFound); // undefined

So sánh find() và filter():

const numbers = [5, 12, 8, 130, 44];

const found = numbers.find(n => n > 10);
console.log(found); // 12 (phần tử đầu tiên > 10)

const filtered = numbers.filter(n => n > 10);
console.log(filtered); // [12, 130, 44] (tất cả phần tử > 10)

6. findIndex() — Tìm vị trí phần tử

findIndex() trả về index của phần tử đầu tiên thoả mãn điều kiện, hoặc -1 nếu không tìm thấy.

const users = [
  { id: 1, name: 'An' },
  { id: 2, name: 'Bình' },
  { id: 3, name: 'Châu' },
];

const index = users.findIndex(u => u.id === 2);
console.log(index); // 1

// Dùng để xoá phần tử khỏi mảng
const idToRemove = 2;
const removeIndex = users.findIndex(u => u.id === idToRemove);
if (removeIndex !== -1) {
  users.splice(removeIndex, 1);
}
console.log(users); // [{ id: 1, name: 'An' }, { id: 3, name: 'Châu' }]

7. some() và every() — Kiểm tra điều kiện

some() — Kiểm tra có ít nhất một phần tử thoả mãn điều kiện.

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

const hasEven = numbers.some(n => n % 2 === 0);
console.log(hasEven); // true (có số chẵn: 2, 4)

const hasNegative = numbers.some(n => n < 0);
console.log(hasNegative); // false

every() — Kiểm tra tất cả phần tử đều thoả mãn điều kiện.

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

const allEven = numbers.every(n => n % 2 === 0);
console.log(allEven); // true

const allPositive = [1, 2, 3, -4, 5].every(n => n > 0);
console.log(allPositive); // false

Ví dụ thực tế — Kiểm tra form input:

const formFields = [
  { name: 'email', value: 'user@example.com', required: true },
  { name: 'password', value: 'secret123', required: true },
  { name: 'name', value: '', required: true },
];

// Kiểm tra tất cả field required đã được điền
const isFormValid = formFields
  .filter(field => field.required)
  .every(field => field.value.trim() !== '');

console.log(isFormValid); // false (name field trống)

// Kiểm tra có field nào đang trống không
const hasEmptyField = formFields
  .filter(field => field.required)
  .some(field => field.value.trim() === '');

console.log(hasEmptyField); // true

8. includes() — Kiểm tra tồn tại

includes() kiểm tra mảng có chứa một giá trị cụ thể không. Trả về true hoặc false.

const fruits = ['🍎', '🍌', '🍊', '🍇'];

console.log(fruits.includes('🍎'));  // true
console.log(fruits.includes('🥝'));  // false

// So sánh với indexOf
console.log(fruits.indexOf('🍌') !== -1); // true (cách cũ)
console.log(fruits.includes('🍌'));        // true (cách mới, dễ đọc hơn)

9. sort() — Sắp xếp mảng

sort() sắp xếp mảng tại chỗ (mutates original array) và trả về mảng đã sắp xếp.

// ⚠️ Mặc định sort() chuyển phần tử thành string rồi so sánh
const numbers = [1, 10, 2, 21, 3];
numbers.sort();
console.log(numbers); // [1, 10, 2, 21, 3] — SAI! Sắp xếp theo string

// ✅ Đúng: truyền compare function
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 2, 3, 10, 21]

// Sắp xếp giảm dần
numbers.sort((a, b) => b - a);
console.log(numbers); // [21, 10, 3, 2, 1]

Sắp xếp mảng object:

const products = [
  { name: 'Laptop', price: 15000000 },
  { name: 'Phone', price: 8000000 },
  { name: 'Tablet', price: 10000000 },
];

// Sắp xếp theo giá tăng dần
products.sort((a, b) => a.price - b.price);
console.log(products);
// [
//   { name: 'Phone', price: 8000000 },
//   { name: 'Tablet', price: 10000000 },
//   { name: 'Laptop', price: 15000000 }
// ]

// Sắp xếp theo tên alphabet
products.sort((a, b) => a.name.localeCompare(b.name));
// [
//   { name: 'Laptop', price: 15000000 },
//   { name: 'Phone', price: 8000000 },
//   { name: 'Tablet', price: 10000000 }
// ]

10. Chaining — Nối nhiều phương thức

Sức mạnh thực sự của array methods là khả năng chain (nối) chúng lại với nhau.

const products = [
  { name: 'Laptop', price: 15000000, category: 'electronics', stock: 5 },
  { name: 'Phone', price: 8000000, category: 'electronics', stock: 0 },
  { name: 'Shirt', price: 300000, category: 'clothing', stock: 10 },
  { name: 'Tablet', price: 10000000, category: 'electronics', stock: 3 },
  { name: 'Shoes', price: 500000, category: 'clothing', stock: 0 },
];

// Bài toán: Lấy tên các sản phẩm điện tử còn hàng, sắp xếp theo giá giảm dần
const result = products
  .filter(p => p.category === 'electronics')  // Lọc sản phẩm điện tử
  .filter(p => p.stock > 0)                   // Lọc còn hàng
  .sort((a, b) => b.price - a.price)           // Sắp xếp giá giảm dần
  .map(p => p.name);                           // Chỉ lấy tên

console.log(result);
// ['Laptop', 'Tablet']

Ví dụ phức tạp hơn — Thống kê đơn hàng:

const orders = [
  { id: 1, items: [{ price: 50000, qty: 2 }, { price: 30000, qty: 1 }] },
  { id: 2, items: [{ price: 200000, qty: 1 }] },
  { id: 3, items: [{ price: 15000, qty: 4 }, { price: 25000, qty: 2 }, { price: 10000, qty: 1 }] },
];

// Tính tổng doanh thu tất cả đơn hàng
const totalRevenue = orders
  .flatMap(order => order.items)  // Làm phẳng items từ tất cả orders
  .reduce((sum, item) => sum + (item.price * item.qty), 0);

console.log(`Tổng doanh thu: ${totalRevenue.toLocaleString('vi-VN')}₫`);
// Tổng doanh thu: 460.000₫

11. flat() và flatMap() — Làm phẳng mảng

flat(depth) — Làm phẳng mảng lồng nhau với độ sâu chỉ định.

const nested = [1, [2, [3, [4]]]];

console.log(nested.flat());        // [1, 2, [3, [4]]] — depth mặc định = 1
console.log(nested.flat(2));       // [1, 2, 3, [4]]
console.log(nested.flat(Infinity)); // [1, 2, 3, 4] — làm phẳng tất cả

flatMap() — Kết hợp map()flat(1) trong một lần gọi.

const sentences = ['Hello World', 'Foo Bar'];

// Tách từng câu thành mảng các từ
const words = sentences.flatMap(s => s.split(' '));
console.log(words); // ['Hello', 'World', 'Foo', 'Bar']

// Tương đương với:
const words2 = sentences.map(s => s.split(' ')).flat();
console.log(words2); // ['Hello', 'World', 'Foo', 'Bar']

12. Một số phương thức hữu ích khác

at() — Lấy phần tử theo index (hỗ trợ index âm)

const arr = [10, 20, 30, 40, 50];

console.log(arr.at(0));   // 10
console.log(arr.at(-1));  // 50 (phần tử cuối)
console.log(arr.at(-2));  // 40 (phần tử áp cuối)

fill() — Điền giá trị vào mảng

// Tạo mảng với giá trị mặc định
const arr = new Array(5).fill(0);
console.log(arr); // [0, 0, 0, 0, 0]

// Điền một phần của mảng
const arr2 = [1, 2, 3, 4, 5];
arr2.fill(0, 1, 3);
console.log(arr2); // [1, 0, 0, 4, 5]

Array.from() — Tạo mảng từ iterable

// Tạo mảng từ string
console.log(Array.from('hello')); // ['h', 'e', 'l', 'l', 'o']

// Tạo mảng số từ 0 đến 9
console.log(Array.from({ length: 10 }, (_, i) => i));
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

// Clone mảng
const original = [1, 2, 3];
const clone = Array.from(original);

with() — Thay đổi phần tử mà không mutate (ES2023)

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

// Thay phần tử tại index 2 thành 99, trả về mảng mới
const newArr = arr.with(2, 99);
console.log(newArr); // [1, 2, 99, 4, 5]
console.log(arr);    // [1, 2, 3, 4, 5] — không thay đổi

So sánh nhanh các phương thức

Phương thứcTrả vềMutate mảng gốc?Khi nào dùng?
forEach()undefinedKhôngThực thi hành động trên mỗi phần tử
map()Mảng mớiKhôngBiến đổi mảng thành mảng mới
filter()Mảng mớiKhôngLọc phần tử theo điều kiện
reduce()Một giá trịKhôngGom mảng thành một giá trị
find()Một phần tửKhôngTìm một phần tử duy nhất
findIndex()Số (index)KhôngTìm vị trí phần tử
some()booleanKhôngKiểm tra có >= 1 phần tử thoả mãn
every()booleanKhôngKiểm tra tất cả phần tử thoả mãn
includes()booleanKhôngKiểm tra giá trị tồn tại
sort()Mảng đã sortSắp xếp mảng
flat()Mảng mớiKhôngLàm phẳng mảng lồng nhau
flatMap()Mảng mớiKhôngMap + flat trong một lần

Performance Tips

  1. Dùng phương thức chuyên biệt thay vì forEach() + iffilter() rõ ràng hơn và có thể tối ưu hơn
  2. Hạn chế chain quá dài — Chain 3-4 phương thức là OK, nhưng 7-8 sẽ khó debug
  3. Dùng for loop khi cần performance tối đa — Với mảng rất lớn (>100K phần tử), for loop nhanh hơn array methods
  4. Tránh mutate mảng gốc — Luôn dùng spread operator [...arr] hoặc Array.from() nếu cần clone
  5. some()every() dừng sớm — Chúng dừng ngay khi tìm thấy kết quả, tiết kiệm thời gian

Kết luận

JavaScript Array Methods là những công cụ không thể thiếu trong hành trang của bất kỳ web developer nào. Chúng giúp code của bạn:

  • Ngắn gọn và dễ đọc — Không còn vòng lặp for dài dòng
  • Dễ bảo trì — Mỗi phương thức có một trách nhiệm rõ ràng
  • Mạnh mẽ — Kết hợp chúng với nhau giải quyết được hầu hết bài toán xử lý dữ liệu

Hãy bắt đầu bằng cách thay thế các vòng lặp for của bạn bằng map(), filter(), và reduce() ngay hôm nay. Bạn sẽ thấy code của mình sạch hơn, chuyên nghiệp hơn và ít lỗi hơn rất nhiều!

Luyện tập: Hãy thử giải các bài toán sau bằng array methods:

  1. Cho mảng số, tính tổng các số chẵn
  2. Cho mảng user, lấy danh sách email của user trên 18 tuổi
  3. Cho mảng chuỗi, đếm số lần xuất hiện của mỗi từ
  4. Cho mảng sản phẩm, tính tổng giá trị tồn kho (price * quantity)
  5. Cho mảng lồng nhau, làm phẳng và loại bỏ duplicate

Recently Used Tools