Duyệt mảng (Array Iteration) trong JavaScript

Javascript căn bản | by Học Javascript

Trong lập trình JavaScript, mảng (Array) là một trong những cấu trúc dữ liệu quan trọng và được sử dụng phổ biến để lưu trữ và quản lý tập hợp dữ liệu. Khi làm việc với mảng, một trong những thao tác cơ bản và quan trọng nhất là duyệt mảng (Array Iteration), tức là lặp qua từng phần tử để thực hiện các thao tác như tính toán, lọc dữ liệu, tìm kiếm hoặc chuyển đổi mảng.

JavaScript cung cấp nhiều cách để duyệt mảng, từ các vòng lặp cơ bản như for, while đến các phương thức có sẵn như forEach(), map(), filter(), reduce(), giúp lập trình viên xử lý dữ liệu hiệu quả hơn. Việc lựa chọn phương pháp duyệt phù hợp không chỉ giúp tối ưu hiệu suất mà còn giúp mã nguồn trở nên dễ đọc, ngắn gọn và dễ bảo trì hơn.

Bài viết này sẽ giúp bạn tìm hiểu các phương pháp duyệt mảng phổ biến trong JavaScript, so sánh ưu nhược điểm của từng phương thức và cách ứng dụng chúng vào thực tế.

Duyệt mảng trong JavaScript

Duyệt mảng (Array Iteration) là quá trình lặp qua từng phần tử trong một mảng để thực hiện các thao tác như truy xuất, thay đổi giá trị, tính toán hoặc lọc dữ liệu. Đây là một kỹ thuật quan trọng trong lập trình vì hầu hết các thao tác xử lý dữ liệu đều liên quan đến việc duyệt qua mảng.

Trong JavaScript, có nhiều cách để duyệt mảng, bao gồm các vòng lặp truyền thống (for, while, do...while) và các phương thức có sẵn (forEach(), map(), filter(), reduce(), v.v.), giúp lập trình viên xử lý dữ liệu hiệu quả hơn.

Tại sao cần duyệt mảng?

Việc duyệt mảng là cần thiết trong nhiều trường hợp khi làm việc với dữ liệu. Một số lý do quan trọng bao gồm:

  • Xử lý dữ liệu từng phần tử trong mảng: Lặp qua từng phần tử để thực hiện các thao tác như in ra màn hình, thay đổi giá trị hoặc kiểm tra điều kiện.
  • Áp dụng các phép toán: Tính tổng, tìm giá trị lớn nhất, nhỏ nhất hoặc thực hiện các phép toán phức tạp hơn.
  • Lọc dữ liệu: Lọc ra các phần tử thỏa mãn điều kiện nhất định (ví dụ: tìm các số chẵn trong một mảng số nguyên).
  • Chuyển đổi mảng: Tạo một mảng mới dựa trên các giá trị của mảng ban đầu (ví dụ: nhân đôi giá trị của từng phần tử).
  • Tối ưu hóa hiệu suất và đơn giản hóa mã nguồn: Sử dụng các phương thức phù hợp giúp mã dễ đọc, dễ bảo trì và tối ưu hiệu suất.

Các phương pháp duyệt mảng phổ biến trong JavaScript

Có hai nhóm phương pháp chính để duyệt mảng:

Duyệt mảng bằng vòng lặp cơ bản

  • for: Vòng lặp truyền thống, cho phép kiểm soát chỉ mục (index) của từng phần tử.
  • while: Vòng lặp tiếp tục chạy khi điều kiện còn đúng.
  • do...while: Tương tự while, nhưng đảm bảo vòng lặp chạy ít nhất một lần.

Duyệt mảng bằng phương thức có sẵn trong JavaScript

  • forEach(): Duyệt qua từng phần tử mà không trả về giá trị.
  • map(): Duyệt qua mảng và trả về một mảng mới sau khi xử lý từng phần tử.
  • filter(): Trả về một mảng mới gồm các phần tử thỏa mãn điều kiện cho trước.
  • reduce(): Tính toán giá trị tích lũy dựa trên các phần tử trong mảng.
  • find(), findIndex(): Tìm phần tử đầu tiên hoặc chỉ mục của phần tử đầu tiên thỏa mãn điều kiện.
  • some(), every(): Kiểm tra điều kiện của một hoặc tất cả các phần tử trong mảng.

Việc lựa chọn phương pháp phù hợp tùy thuộc vào yêu cầu cụ thể của bài toán và hiệu suất mong muốn. Trong các phần tiếp theo, chúng ta sẽ đi sâu vào từng phương pháp duyệt mảng và cách sử dụng chúng hiệu quả.

Duyệt mảng bằng vòng lặp cơ bản trong JavaScript

Vòng lặp cơ bản trong JavaScript giúp duyệt mảng bằng cách lặp qua từng phần tử thông qua chỉ mục (index). Chúng ta có thể dùng các vòng lặp như for, while, và do...while để thực hiện điều này.

for loop (Vòng lặp for thông thường)

Vòng lặp for là một trong những cách phổ biến nhất để duyệt mảng. Cấu trúc của vòng lặp for gồm 3 phần:

for (khởi tạo; điều kiện lặp; bước lặp) {
    // Thực hiện hành động với từng phần tử trong mảng
}
  • Khởi tạo (let i = 0): Bắt đầu vòng lặp với giá trị chỉ mục (index).
  • Điều kiện lặp (i < array.length): Kiểm tra xem chỉ mục có nằm trong giới hạn của mảng không.
  • Bước lặp (i++): Tăng hoặc giảm chỉ mục sau mỗi lần lặp.

Ví dụ: Duyệt qua từng phần tử trong mảng và in ra màn hình

let numbers = [10, 20, 30, 40, 50];

for (let i = 0; i < numbers.length; i++) {
    console.log(numbers[i]); 
}

Ưu điểm của for loop:
Kiểm soát tốt chỉ mục của mảng.
Có thể nhảy bước (i += 2) hoặc dừng sớm (break).

Ví dụ: Duyệt qua mảng với bước nhảy 2

let numbers = [10, 20, 30, 40, 50, 60];

for (let i = 0; i < numbers.length; i += 2) {
    console.log(numbers[i]); 
}
Kết quả:
10
30
50

while loop (Vòng lặp while)

Vòng lặp while lặp qua các phần tử dựa trên một điều kiện kiểm tra. Vòng lặp tiếp tục chạy khi điều kiện còn đúng (true).

Cấu trúc vòng lặp while

while (điều kiện) {
    // Thực hiện hành động với từng phần tử trong mảng
}
Ví dụ: Duyệt mảng bằng vòng lặp while
let numbers = [10, 20, 30, 40, 50];
let i = 0;

while (i < numbers.length) {
    console.log(numbers[i]);
    i++; // Tăng chỉ mục sau mỗi lần lặp
}
Kết quả:
10
20
30
40
50

Ưu điểm của while loop:
Thích hợp khi không biết trước số lần lặp.
Có thể sử dụng điều kiện linh hoạt hơn so với for.

Ví dụ: Dừng vòng lặp khi gặp số lớn hơn 30

let numbers = [10, 20, 30, 40, 50];
let i = 0;

while (i < numbers.length && numbers[i] <= 30) {
    console.log(numbers[i]);
    i++;
}

Kết quả:

do...while loop (Vòng lặp do...while)

Vòng lặp do...while giống với while, nhưng luôn chạy ít nhất một lần trước khi kiểm tra điều kiện lặp.

Cấu trúc vòng lặp do...while

do {
    // Thực hiện hành động với phần tử đầu tiên trong mảng
} while (điều kiện);

Ví dụ: Duyệt qua mảng bằng vòng lặp do...while

let numbers = [10, 20, 30, 40, 50];
let i = 0;

do {
    console.log(numbers[i]);
    i++;
} while (i < numbers.length);

Kết quả

Ưu điểm của do...while loop:
Đảm bảo ít nhất một lần lặp, ngay cả khi điều kiện sai ngay từ đầu.

Ví dụ: Trường hợp điều kiện sai ngay từ đầu

let i = 10; // Chỉ mục lớn hơn độ dài mảng
let numbers = [1, 2, 3, 4, 5];

do {
    console.log(numbers[i]); // Vẫn chạy ít nhất một lần (sẽ bị lỗi do chỉ mục không hợp lệ)
    i++;
} while (i < numbers.length);
Kết quả:
undefined (lỗi do i > numbers.length ngay từ đầu)

Lưu ý: Trong trường hợp này, nếu dùng while, vòng lặp sẽ không chạy lần nào.

Khi nào nên sử dụng từng vòng lặp?

Vòng lặp Khi nào nên dùng?
for Khi cần duyệt qua tất cả phần tử trong mảng một cách có kiểm soát (có thể nhảy bước hoặc dừng sớm).
while Khi không biết trước số lần lặp và muốn dừng dựa trên một điều kiện động.
do...while Khi muốn đảm bảo rằng vòng lặp chạy ít nhất một lần.
  • Dùng for khi biết rõ số lần lặp hoặc cần truy cập chỉ mục (index).
  • Dùng while khi điều kiện lặp không xác định trước.
  • Dùng do...while khi muốn chắc chắn ít nhất một lần thực thi vòng lặp.

Duyệt mảng bằng các phương thức có sẵn trong JavaScript

Ngoài các vòng lặp cơ bản (for, while, do...while), JavaScript còn cung cấp nhiều phương thức có sẵn giúp duyệt mảng một cách hiệu quả, ngắn gọn và dễ đọc. Dưới đây là các phương thức phổ biến dùng để duyệt mảng.

forEach() – Duyệt mảng không trả về giá trị

Phương thức forEach() giúp duyệt qua từng phần tử của mảng và thực hiện một hành động nhất định mà không tạo ra mảng mới.

Cú pháp:

array.forEach(callback(currentValue, index, array));
  • currentValue: Giá trị của phần tử hiện tại.
  • index (tùy chọn): Chỉ mục của phần tử hiện tại.
  • array (tùy chọn): Chính mảng đang được duyệt.

Ví dụ: In từng phần tử trong mảng

let numbers = [10, 20, 30, 40];

numbers.forEach((num) => {
    console.log(num);
});
Kết quả:
10
20
30
40

Lưu ý: forEach() không trả về giá trị, vì vậy nó không thể dùng để tạo mảng mới.

map() – Duyệt mảng và tạo mảng mới

Phương thức map() duyệt qua từng phần tử trong mảng, thực hiện một hành động trên từng phần tử, và trả về một mảng mới.

Cú pháp:

let newArray = array.map(callback(currentValue, index, array));
Ví dụ: Nhân đôi giá trị trong mảng
let numbers = [1, 2, 3, 4];
let doubled = numbers.map(num => num * 2);

console.log(doubled);

Kết quả:

[2, 4, 6, 8]

So sánh với forEach():

  • map() tạo ra mảng mới, trong khi forEach() chỉ thực hiện hành động mà không thay đổi dữ liệu.

filter() – Duyệt mảng và lọc phần tử thỏa mãn điều kiện

Phương thức filter() giúp tạo ra một mảng mới chứa các phần tử thoả mãn điều kiện.

Cú pháp:

let newArray = array.filter(callback(currentValue, index, array));

Ví dụ: Lọc ra các số chẵn trong mảng

let numbers = [10, 15, 20, 25, 30];
let evenNumbers = numbers.filter(num => num % 2 === 0);

console.log(evenNumbers);

Kết quả:

[10, 20, 30]

reduce() – Duyệt mảng và tính toán tích lũy

Phương thức reduce() giúp tính toán tích lũy từ các phần tử trong mảng, thường dùng để tính tổng, nhân, gom nhóm dữ liệu.

Cú pháp:

let result = array.reduce(callback(accumulator, currentValue, index, array), initialValue);
  • accumulator: Giá trị tích lũy (kết quả của lần tính toán trước).
  • currentValue: Giá trị hiện tại.
  • initialValue: Giá trị khởi tạo (nếu có).

Ví dụ: Tính tổng các số trong mảng

let numbers = [1, 2, 3, 4, 5];
let sum = numbers.reduce((acc, num) => acc + num, 0);

console.log(sum);
Kết quả:
15

Ứng dụng: reduce() rất hữu ích khi cần gom dữ liệu hoặc tính toán giá trị tổng hợp.

find() – Tìm phần tử đầu tiên thỏa mãn điều kiện

Phương thức find() giúp tìm phần tử đầu tiên trong mảng thỏa mãn điều kiện.

Cú pháp:

let value = array.find(callback(currentValue, index, array));

Ví dụ: Tìm số đầu tiên lớn hơn 20

let numbers = [10, 15, 25, 30];
let result = numbers.find(num => num > 20);

console.log(result);
Kết quả:
25

So sánh với filter():

  • find() chỉ trả về một phần tử đầu tiên, còn filter() trả về tất cả phần tử phù hợp.

findIndex() – Tìm chỉ mục phần tử đầu tiên thỏa mãn điều kiện

Tương tự find(), nhưng findIndex() trả về vị trí (chỉ mục) thay vì giá trị.

Ví dụ: Tìm vị trí số đầu tiên lớn hơn 20

let numbers = [10, 15, 25, 30];
let index = numbers.findIndex(num => num > 20);

console.log(index);
Kết quả:
2

some() – Kiểm tra có ít nhất một phần tử thỏa mãn điều kiện

Phương thức some() kiểm tra xem có ít nhất một phần tử trong mảng thỏa mãn điều kiện hay không.

Cú pháp:

let result = array.some(callback(currentValue, index, array));

Ví dụ: Kiểm tra có số lớn hơn 50 không

let numbers = [10, 20, 30, 40];
let hasLargeNumber = numbers.some(num => num > 50);

console.log(hasLargeNumber);
Kết quả:
false

every() – Kiểm tra xem tất cả phần tử có thỏa mãn điều kiện không

Phương thức every() kiểm tra xem tất cả phần tử trong mảng có thỏa mãn điều kiện hay không.

Cú pháp:

let result = array.every(callback(currentValue, index, array));

Ví dụ: Kiểm tra tất cả số có lớn hơn 5 không

let numbers = [10, 20, 30, 40];
let allGreaterThanFive = numbers.every(num => num > 5);

console.log(allGreaterThanFive);

Kết quả:

true

So sánh some()every():

  • some() trả về true nếu ít nhất một phần tử thỏa mãn điều kiện.
  • every() chỉ trả về true nếu tất cả phần tử thỏa mãn điều kiện.
Phương thức Công dụng Kết quả trả về
forEach() Duyệt mảng và thực hiện hành động Không trả về giá trị
map() Tạo mảng mới bằng cách biến đổi từng phần tử Mảng mới
filter() Tạo mảng mới chứa các phần tử phù hợp Mảng mới
reduce() Tính toán tích lũy từ các phần tử trong mảng Một giá trị tổng hợp
find() Tìm phần tử đầu tiên thỏa mãn điều kiện Giá trị phần tử
findIndex() Tìm vị trí phần tử đầu tiên thỏa mãn điều kiện Chỉ mục
some() Kiểm tra có ít nhất một phần tử thỏa mãn điều kiện không true / false
every() Kiểm tra tất cả phần tử có thỏa mãn điều kiện không true / false

So sánh hiệu suất giữa các phương thức duyệt mảng trong JavaScript

Khi làm việc với mảng trong JavaScript, việc chọn đúng phương thức duyệt mảng không chỉ ảnh hưởng đến tính dễ đọc của mã nguồn mà còn tác động đến hiệu suất, đặc biệt với các mảng lớn. Dưới đây là phân tích hiệu suất và khi nào nên sử dụng từng phương thức.

for vs. forEach vs. map: Khi nào nên dùng từng loại?

Phương thức Tốc độ Dễ đọc Tạo mảng mới Thay đổi mảng gốc Khi nào nên dùng?
for Nhanh nhất Trung bình Không Có thể Khi cần tối ưu hiệu suất, tránh gọi hàm callback nhiều lần
forEach Chậm hơn for Dễ đọc Không Có thể Khi duyệt qua mảng mà không cần trả về giá trị
map Chậm hơn forEach Rất dễ đọc Không Khi cần tạo mảng mới từ mảng gốc

Nhận xét:

  • for nhanh nhất vì không sử dụng callback function.
  • forEach dễ đọc hơn for, nhưng không tạo mảng mới.
  • map dễ đọc nhất, nhưng tạo mảng mới, gây tốn bộ nhớ nếu không cần thiết.

Ví dụ: So sánh for, forEach, và map

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

// Duyệt bằng for
let doubledFor = [];
for (let i = 0; i < numbers.length; i++) {
    doubledFor.push(numbers[i] * 2);
}

// Duyệt bằng forEach
let doubledForEach = [];
numbers.forEach(num => {
    doubledForEach.push(num * 2);
});

// Duyệt bằng map
let doubledMap = numbers.map(num => num * 2);

console.log(doubledFor);       // [2, 4, 6, 8, 10]
console.log(doubledForEach);   // [2, 4, 6, 8, 10]
console.log(doubledMap);       // [2, 4, 6, 8, 10]

Kết quả :

Nhận xét:

  • for nhanh nhất vì không sử dụng callback function.
  • forEach dễ đọc hơn for, nhưng không tạo mảng mới.
  • map dễ đọc nhất, nhưng tạo mảng mới, gây tốn bộ nhớ nếu không cần thiết.

Hiệu suất giữa forEach(), map(), filter(), reduce()

Khi làm việc với mảng lớn, hiệu suất là yếu tố quan trọng. Dưới đây là so sánh hiệu suất của các phương thức phổ biến.

Phương thức Mục đích Hiệu suất (so với for)
forEach Lặp qua mảng Chậm hơn for do callback function
map Tạo mảng mới từ mảng cũ Chậm hơn forEach do tạo mảng mới
filter Lọc phần tử theo điều kiện Chậm hơn forEach do tạo mảng mới
reduce Tích lũy giá trị từ mảng Chậm hơn for, nhưng gọn gàng

Ví dụ: So sánh map(), filter(), reduce()

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

// map(): Nhân đôi số trong mảng
let doubled = numbers.map(num => num * 2);

// filter(): Lọc số lớn hơn 2
let filtered = numbers.filter(num => num > 2);

// reduce(): Tính tổng
let sum = numbers.reduce((acc, num) => acc + num, 0);

console.log(doubled);  // [2, 4, 6, 8, 10]
console.log(filtered); // [3, 4, 5]
console.log(sum);      // 15

Nhận xét:

  • mapfilter tạo mảng mới, gây tiêu tốn bộ nhớ.
  • reduce phù hợp cho tính toán tổng hợp dữ liệu, nhưng có thể khó đọc hơn.

Ứng dụng thực tế của Duyệt mảng trong JavaScript

Duyệt mảng là kỹ thuật rất quan trọng trong lập trình, được sử dụng trong nhiều bài toán thực tế.

Xử lý dữ liệu trong danh sách sản phẩm

Giả sử có danh sách sản phẩm, ta có thể sử dụng map() để hiển thị giá sản phẩm với đơn vị tiền tệ.

Ví dụ:

let products = [
    { name: "Laptop", price: 1000 },
    { name: "Phone", price: 500 }
];

let productNames = products.map(product => `${product.name} - $${product.price}`);

console.log(productNames);

Kết quả:

["Laptop - $1000", "Phone - $500"]

Tính toán tổng số tiền trong giỏ hàng

Sử dụng reduce() để tính tổng tiền.

Ví dụ:

let cart = [
    { name: "Laptop", price: 1000 },
    { name: "Phone", price: 500 }
];

let totalPrice = cart.reduce((total, item) => total + item.price, 0);

console.log(totalPrice);

Kết quả:

1500

Kết bài

Duyệt mảng là một kỹ thuật quan trọng trong JavaScript, giúp xử lý dữ liệu hiệu quả hơn trong lập trình. Tùy vào yêu cầu cụ thể, ta có thể lựa chọn các phương pháp khác nhau:

  • Vòng lặp for giúp tối ưu hiệu suất, phù hợp với các bài toán phức tạp.
  • forEach() đơn giản và dễ đọc, nhưng không trả về giá trị.
  • map(), filter(), reduce() mạnh mẽ khi cần tạo hoặc xử lý dữ liệu trong mảng.
  • Hiệu suất là yếu tố quan trọng khi làm việc với mảng lớn, cần lựa chọn phương thức phù hợp để tối ưu.

Việc hiểu rõ và áp dụng đúng phương pháp duyệt mảng giúp lập trình viên viết code ngắn gọn hơn, dễ bảo trì hơn và tăng hiệu suất ứng dụng.

Bài viết liên quan

  • 2