+6

Xài find() hay xài Map, đâu mới là lựa chọn tốt nhất?

Bạn là Dev JavaScript? Chắc hẳn bạn cũng đã từng xài cái phương thức find() rồi đúng không nào? Nhưng mà bạn có biết là nó chạy chậm như rùa không? 🐢

Thế này nhé, find() nó tìm kiếm từng phần tử một trong mảng cho đến khi gặp cái nó cần. Tệ nhất là khi thằng cần tìm nó nằm tận cuối, thì phải lục tung cả đám lên, mất thời gian kinh khủng, độ phức tạp O(n) luôn. Mệt! 😩 Ví dụ nhé, giả sử bạn có một mảng các con số từ 1 đến 1 triệu, mà bạn muốn tìm xem số 999,999 có trong đó không. Nếu dùng find(), nó phải duyệt qua từng phần tử một, từ đầu đến cuối mảng. Trời ơi, mất cả buổi luôn! 😫

Nhưng mà đừng lo, anh em mình có giải pháp ngon lành cành đào hơn nhiều. Đó chính là hash map, một kiểu lưu trữ cực kỳ nhanh. Nó như kiểu từ điển ấy, tra cái key là ra value liền. Thêm, xóa cũng nhanh như điện xẹt. Tất tần tật chỉ tốn O(1), ngon ơ phải không nào? 😎 Cứ tưởng tượng hash map như cuốn từ điển tiếng Anh ấy. Muốn tra nghĩa của một từ, chỉ cần mở ra trang có từ đó, chứ có cần phải lật giở từng trang đâu. Nhanh gọn lẹ, đúng không nào? 😄

Trong JavaScript thì có sẵn Map rồi, xài cái này đi bạn. Nó linh hoạt cực, key muốn là gì cũng được, không như mảng chỉ xài số thôi. Xử lý mấy cái dữ liệu phức tạp hay cần tìm kiếm nhanh là Map xử đẹp luôn. 💪 Ví dụ nè, giả sử bạn có một đống thông tin sinh viên, mỗi sinh viên có ID riêng. Bạn muốn lưu trữ và truy xuất thông tin dựa theo ID. Nếu dùng mảng, mỗi lần tìm kiếm lại phải duyệt qua cả đám, mệt lắm. Nhưng với Map, cứ cho ID vào làm key, thông tin vào làm value, thế là xong. Lần sau muốn lấy thông tin của sinh viên nào, cứ get() ra là có liền. 😉

Chuyển dữ liệu sang Map cũng dễ ợt. Lướt qua 1 lượt, nhớ là key phải unique nha. Xong xuôi rồi thì cứ get() mà lấy, O(1) tất. Kiểm tra xem data có tồn tại không thì has() một phát, cũng O(1) luôn, đỡ phải dùng includes() chạy O(n). 😄 Ví dụ nha, có một mảng các object chứa thông tin sách, mỗi cuốn có một mã số ISBN duy nhất. Bạn muốn chuyển sang Map để truy xuất nhanh hơn. Cứ thế này là được:

const books = [
  { isbn: "978-1-56619-909-4", title: "Đắc Nhân Tâm" },
  { isbn: "978-0-7334-2609-4", title: "Nhà Giả Kim" },
  { isbn: "978-0-14-103940-8", title: "Hoàng tử bé" }
];

const bookMap = new Map(books.map(book => [book.isbn, book]));

Vậy là xong, giờ muốn tìm sách nào, cứ lấy ISBN ra get() là có liền:

const myBook = bookMap.get("978-0-14-103940-8");
console.log(myBook.title); // "Hoàng tử bé"

Nhanh gọn lẹ phải không nào? 😄

Nói tóm lại, Map đúng là "đỉnh của chóp" so với mảng và find() khi xử lý với truy xuất data, nhất là với mấy bộ dữ liệu bự chà bá. Tìm kiếm cực nhanh, luôn luôn O(1), thích không nào? 😉 Nó giống như bạn có một cuốn sổ tay ghi chép đầy đủ, muốn tìm gì cứ lật ra trang đó là có, khỏi cần phải dò từng trang một. Tiện lợi cực kỳ luôn ấy chứ! 😎

Nhưng mà khoan đã, cái gì cũng có mặt trái của nó, Map cũng vậy. Đúng là nó nhanh thật, nhưng trong một số trường hợp, nó lại gây phiền toái hoặc thậm chí còn chậm hơn cả find() nữa cơ. 😅

Ví dụ nè, giả sử bạn có một mảng các số nguyên, và bạn muốn tìm số lớn nhất trong đó. Nếu dùng find(), bạn chỉ cần duyệt qua mảng một lần và so sánh từng phần tử, cuối cùng sẽ ra được số lớn nhất. Nhưng nếu dùng Map, bạn phải tạo một Map mới, rồi duyệt qua mảng để đưa các phần tử vào Map, cuối cùng mới lấy ra được số lớn nhất. Rõ ràng là phức tạp và tốn thời gian hơn nhiều so với find() rồi. 😓

Hay là trường hợp bạn muốn tìm tất cả các phần tử thỏa mãn một điều kiện nào đó trong mảng. Với find(), bạn chỉ cần lọc mảng theo điều kiện là ra. Nhưng với Map, bạn lại phải tạo một Map mới, rồi duyệt qua Map để tìm các phần tử thỏa mãn, cuối cùng mới ra được kết quả. Lại mất công, mất thời gian hơn find() rồi. 😞

Nên là, tùy vào từng trường hợp cụ thể mà bạn nên cân nhắc sử dụng Map hay find() cho phù hợp. Không phải lúc nào Map cũng là lựa chọn tối ưu đâu nha. 😉

Dù sao thì Map vẫn là một công cụ rất mạnh mẽ và hữu ích trong lập trình, đặc biệt là khi làm việc với các cặp key-value. Nhưng đừng lạm dụng quá, kẻo lại phản tác dụng nha. 😄 Thường mình luôn lựa chọn Map khi có một logic sử dụng lại mạng đó nhiều lần thì mình sẽ tạo 1 Map và sau đó là vèo vèo vèo.

Hy vọng bài viết này giúp bạn hiểu thêm về sức mạnh cũng như điểm yếu của Map, và biết cách tận dụng nó sao cho hiệu quả nhé. Nếu thấy hay thì đừng quên chia sẻ cho anh em cùng biết nha. Chúc mọi người một ngày vui vẻ, ahihi! 😊

Bạn là Dev JavaScript? Chắc hẳn bạn cũng đã từng xài cái phương thức find() rồi đúng không nào? Nhưng mà bạn có biết là nó chạy chậm như rùa không? 🐢

Thế này nhé, find() nó tìm kiếm từng phần tử một trong mảng cho đến khi gặp cái nó cần. Tệ nhất là khi thằng cần tìm nó nằm tận cuối, thì phải lục tung cả đám lên, mất thời gian kinh khủng, độ phức tạp O(n) luôn. Mệt! 😩 Ví dụ nhé, giả sử bạn có một mảng các con số từ 1 đến 1 triệu, mà bạn muốn tìm xem số 999,999 có trong đó không. Nếu dùng find(), nó phải duyệt qua từng phần tử một, từ đầu đến cuối mảng. Trời ơi, mất cả buổi luôn! 😫

Nhưng mà đừng lo, anh em mình có giải pháp ngon lành cành đào hơn nhiều. Đó chính là hash map, một kiểu lưu trữ cực kỳ nhanh. Nó như kiểu từ điển ấy, tra cái key là ra value liền. Thêm, xóa cũng nhanh như điện xẹt. Tất tần tật chỉ tốn O(1), ngon ơ phải không nào? 😎 Cứ tưởng tượng hash map như cuốn từ điển tiếng Anh ấy. Muốn tra nghĩa của một từ, chỉ cần mở ra trang có từ đó, chứ có cần phải lật giở từng trang đâu. Nhanh gọn lẹ, đúng không nào? 😄

Trong JavaScript thì có sẵn Map rồi, xài cái này đi bạn. Nó linh hoạt cực, key muốn là gì cũng được, không như mảng chỉ xài số thôi. Xử lý mấy cái dữ liệu phức tạp hay cần tìm kiếm nhanh là Map xử đẹp luôn. 💪 Ví dụ nè, giả sử bạn có một đống thông tin sinh viên, mỗi sinh viên có ID riêng. Bạn muốn lưu trữ và truy xuất thông tin dựa theo ID. Nếu dùng mảng, mỗi lần tìm kiếm lại phải duyệt qua cả đám, mệt lắm. Nhưng với Map, cứ cho ID vào làm key, thông tin vào làm value, thế là xong. Lần sau muốn lấy thông tin của sinh viên nào, cứ get() ra là có liền. 😉

Chuyển dữ liệu sang Map cũng dễ ợt. Lướt qua 1 lượt, nhớ là key phải unique nha. Xong xuôi rồi thì cứ get() mà lấy, O(1) tất. Kiểm tra xem data có tồn tại không thì has() một phát, cũng O(1) luôn, đỡ phải dùng includes() chạy O(n). 😄 Ví dụ nha, có một mảng các object chứa thông tin sách, mỗi cuốn có một mã số ISBN duy nhất. Bạn muốn chuyển sang Map để truy xuất nhanh hơn. Cứ thế này là được:

const books = [
  { isbn: "978-1-56619-909-4", title: "Đắc Nhân Tâm" },
  { isbn: "978-0-7334-2609-4", title: "Nhà Giả Kim" },
  { isbn: "978-0-14-103940-8", title: "Hoàng tử bé" }
];

const bookMap = new Map(books.map(book => [book.isbn, book]));

Vậy là xong, giờ muốn tìm sách nào, cứ lấy ISBN ra get() là có liền:

const myBook = bookMap.get("978-0-14-103940-8");
console.log(myBook.title); // "Hoàng tử bé"

Nhanh gọn lẹ phải không nào? 😄

Nói tóm lại, Map đúng là "đỉnh của chóp" so với mảng và find() khi xử lý với truy xuất data, nhất là với mấy bộ dữ liệu bự chà bá. Tìm kiếm cực nhanh, luôn luôn O(1), thích không nào? 😉 Nó giống như bạn có một cuốn sổ tay ghi chép đầy đủ, muốn tìm gì cứ lật ra trang đó là có, khỏi cần phải dò từng trang một. Tiện lợi cực kỳ luôn ấy chứ! 😎

Nhưng mà khoan đã, cái gì cũng có mặt trái của nó, Map cũng vậy. Đúng là nó nhanh thật, nhưng trong một số trường hợp, nó lại gây phiền toái hoặc thậm chí còn chậm hơn cả find() nữa cơ. 😅

Ví dụ nè, giả sử bạn có một mảng các số nguyên, và bạn muốn tìm số lớn nhất trong đó. Nếu dùng find(), bạn chỉ cần duyệt qua mảng một lần và so sánh từng phần tử, cuối cùng sẽ ra được số lớn nhất. Nhưng nếu dùng Map, bạn phải tạo một Map mới, rồi duyệt qua mảng để đưa các phần tử vào Map, cuối cùng mới lấy ra được số lớn nhất. Rõ ràng là phức tạp và tốn thời gian hơn nhiều so với find() rồi. 😓

Hay là trường hợp bạn muốn tìm tất cả các phần tử thỏa mãn một điều kiện nào đó trong mảng. Với find(), bạn chỉ cần lọc mảng theo điều kiện là ra. Nhưng với Map, bạn lại phải tạo một Map mới, rồi duyệt qua Map để tìm các phần tử thỏa mãn, cuối cùng mới ra được kết quả. Lại mất công, mất thời gian hơn find() rồi. 😞

Nên là, tùy vào từng trường hợp cụ thể mà bạn nên cân nhắc sử dụng Map hay find() cho phù hợp. Không phải lúc nào Map cũng là lựa chọn tối ưu đâu nha. 😉

Dù sao thì Map vẫn là một công cụ rất mạnh mẽ và hữu ích trong lập trình, đặc biệt là khi làm việc với các cặp key-value. Nhưng đừng lạm dụng quá, kẻo lại phản tác dụng nha. 😄 Thường mình luôn lựa chọn Map khi có một logic sử dụng lại mạng đó nhiều lần thì mình sẽ tạo 1 Map và sau đó là vèo vèo vèo.

Hy vọng bài viết này giúp bạn hiểu thêm về sức mạnh cũng như điểm yếu của Map, và biết cách tận dụng nó sao cho hiệu quả nhé. Nếu thấy hay thì đừng quên chia sẻ cho anh em cùng biết nha. Chúc mọi người một ngày vui vẻ, ahihi! 😊


Bổ sung và trả lời commnet:

Cảm ơn bạn đã góp ý @duckhanh0712. Mình hoàn toàn đồng ý với quan điểm của bạn. Trong một số trường hợp, find() vẫn là lựa chọn tốt hơn Map. Ví dụ như khi chỉ cần tìm kiếm một lần duy nhất trong mảng, hoặc khi cần sử dụng các toán tử so sánh như >, <, !==, regex... thì find() sẽ đơn giản và hiệu quả hơn.

Tuy nhiên, mình vẫn muốn nhấn mạnh rằng trong những trường hợp cần tìm kiếm nhiều lần trên một tập dữ liệu lớn, Map sẽ cho hiệu suất vượt trội hơn hẳn. Để minh họa rõ hơn, mình xin đưa ra một ví dụ cụ thể như sau:

Giả sử chúng ta có một mảng chứa thông tin của 1 triệu người dùng, mỗi người dùng có một ID duy nhất. Nhiệm vụ của chúng ta là viết một hàm để tìm kiếm thông tin người dùng dựa trên ID, và hàm này sẽ được gọi rất nhiều lần trong quá trình chạy chương trình.

Ví dụ: Người dùng up dữ liệu bằng csv (hàng triệu record là chuyện quá bt), csv được lưu lên s3 trước sau đó chạy batch job để lấy dữ liệu từ csv lên và xử lý, trong quá trình xử lý cần phải tìm kiếm thông tin các kiểu tùy vào bài toán, thì Map sẽ giúp ích rất nhiều. Trong thực tế thì không phải lúc nào cũng đẩy sáng DB và query vì có nhiều lúc NodeJS cũng giúp ích rất nhiều mà vẫn ko vi phạm nguyên tắc như Non Bloking IO... ví xử lý là quá nhanh.

Nếu sử dụng find(), mỗi lần gọi hàm, chúng ta sẽ phải duyệt qua cả triệu phần tử trong mảng để tìm ID tương ứng. Với độ phức tạp O(n), thời gian tìm kiếm sẽ tăng tuyến tính theo kích thước dữ liệu. Nếu hàm được gọi 1000 lần chẳng hạn, tổng thời gian tìm kiếm sẽ là 1000 * O(n), rất lâu và tốn kém. (Tất nhiên lúc này các bạn sẽ đẩy thẳng xuống DB và tương lai sẽ tối ưu Query để lấy nó lên, nhưng đôi khi design nó thế nó bắt mình phải clean data trước khi lưu vào DB theo format nào đó, và Map sẽ giúp ích rất nhiều trong việc này)

Ngược lại, nếu chúng ta tạo một Map từ mảng ban đầu, với ID làm key và thông tin người dùng làm value, thì mỗi lần tìm kiếm chỉ mất O(1). Dù cho có gọi hàm 1000 lần đi nữa, tổng thời gian tìm kiếm cũng chỉ là 1000 * O(1), nhanh hơn rất nhiều so với find().

Code minh họa:

// Mảng chứa 1 triệu người dùng
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  // ...
  { id: 1000000, name: "John" }
];

// Tạo Map từ mảng
const userMap = new Map(users.map(user => [user.id, user]));

// Hàm tìm kiếm bằng find()
function findUser(id) {
  return users.find(user => user.id === id);
}

// Hàm tìm kiếm bằng Map
function getUserById(id) {
  return userMap.get(id);
}

// Tìm kiếm 1000 lần bằng find()
console.time("find");
for (let i = 0; i < 1000; i++) {
  findUser(Math.floor(Math.random() * 1000000) + 1);
}
console.timeEnd("find");

// Tìm kiếm 1000 lần bằng Map
console.time("map");
for (let i = 0; i < 1000; i++) {
  getUserById(Math.floor(Math.random() * 1000000) + 1);
}
console.timeEnd("map");

Kết quả chạy thử trên máy mình như sau:

find: 10.028s
map: 0.534ms

Rõ ràng là Map nhanh hơn find() rất nhiều lần trong trường hợp này. Thời gian tìm kiếm ~20000 lần bằng find() lên tới hơn 10 giây, trong khi với Map chỉ mất chưa đầy 0.534ms -> sức mạnh của Map trong những tình huống cần tìm kiếm nhiều lần trên bộ dữ liệu lớn.

Tất nhiên, như bạn đã nói, việc tạo Map ban đầu cũng tốn thời gian và bộ nhớ. Nhưng nếu chúng ta cần tìm kiếm nhiều lần, thì chi phí này sẽ nhanh chóng được bù đắp bởi tốc độ tìm kiếm vượt trội sau đó. Hơn nữa, nếu dữ liệu không thay đổi, chúng ta hoàn toàn có thể tạo Map một lần và tái sử dụng nhiều lần, giúp tối ưu hóa hiệu suất tổng thể.

Một ưu điểm nữa của Map là khả năng lưu trữ key với nhiều kiểu dữ liệu khác nhau, không chỉ số hay chuỗi như trong object thông thường. Ví dụ, chúng ta có thể dùng chính object làm key, như trong trường hợp cần map giữa hai object có quan hệ 1-1:

const user1 = { id: 1, name: "Alice" };
const user2 = { id: 2, name: "Bob" };
const user3 = { id: 3, name: "Charlie" };

const userMessages = new Map();
userMessages.set(user1, ["Hello", "How are you?"]);
userMessages.set(user2, ["Hi", "I'm fine, thanks!"]);
userMessages.set(user3, ["Hey", "Long time no see!"]);

console.log(userMessages.get(user1)); // ["Hello", "How are you?"]

Trong ví dụ trên, chúng ta dùng chính các object user làm key để lưu trữ các tin nhắn tương ứng trong Map. Điều này không thể làm được với object thông thường, vì key của object chỉ có thể là string hoặc symbol.

Tóm lại, mặc dù find() vẫn có những trường hợp sử dụng hợp lý, nhưng với những bài toán cần tìm kiếm nhiều lần trên dữ liệu lớn và phức tạp, Map chắc chắn là một sự lựa chọn đáng cân nhắc để tối ưu performance. Hy vọng những chia sẻ trên đây của mình đã giúp làm rõ hơn về ưu và nhược điểm của Map cũng như những use case điển hình của nó. Một lần nữa cảm ơn bạn đã đóng góp ý kiến để bài viết được hoàn thiện hơn! 😊


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí