Lập trình · 25/09/2026

JavaScript sort(): Sắp xếp số đúng cách và giữ nguyên mảng gốc

Một danh sách số lượng có các giá trị 4, 25 và 100 nhưng sau khi gọi sort() lại đưa 100 lên đầu. Đây không nhất thiết là lỗi dữ liệu: JavaScript không mặc định sắp xếp mảng theo giá trị số. Ngoài thứ tự, cần chú ý sort() có làm thay đổi mảng mà nơi khác đang sử dụng hay không.

JavaScript sort(): Sắp xếp số đúng cách và giữ nguyên mảng gốc

Một danh sách số lượng có các giá trị 4, 25 và 100 nhưng sau khi gọi sort() lại đưa 100 lên đầu. Đây không nhất thiết là lỗi dữ liệu: JavaScript không mặc định sắp xếp mảng theo giá trị số. Ngoài thứ tự, cần chú ý sort() có làm thay đổi mảng mà nơi khác đang sử dụng hay không.

1. Xác định quy tắc trước khi sắp xếp

Theo tài liệu Array.prototype.sort(), nếu không truyền hàm so sánh, các phần tử được so sánh theo biểu diễn chuỗi và thứ tự đơn vị mã UTF-16. Phương thức cũng sắp xếp trực tiếp trên mảng hiện có.

const quantities = [4, 25, 100];
const defaultOrder = [...quantities].sort();
const numericOrder = [...quantities].sort((a, b) => a - b);

console.log(defaultOrder); // [100, 25, 4]
console.log(numericOrder); // [4, 25, 100]
console.log(quantities);   // [4, 25, 100]

Các chú thích là kết quả mong đợi. Ví dụ dùng bản sao mảng để hai phép sắp xếp không ảnh hưởng lẫn nhau. Với mảng các số hữu hạn, a - b cho thứ tự tăng dần; b - a cho thứ tự giảm dần.

2. Đừng trả boolean từ hàm so sánh

Hàm so sánh cần biểu đạt ba trường hợp: đứng trước, đứng sau hoặc ngang nhau. Biểu thức a > b chỉ trả true hoặc false, không thể hiện đầy đủ quan hệ đó. Một hàm sai hợp đồng có thể cho kết quả không đáng tin cậy giữa các môi trường.

Không sửa dữ liệu hoặc gọi API trong comparator. Hãy tính sẵn giá trị cần so sánh trước khi sắp xếp, để cùng một cặp đầu vào có cùng kết quả và dễ viết kiểm thử.

3. Tách việc kiểm tra dữ liệu khỏi việc sắp xếp

Ví dụ tiếp theo chỉ chấp nhận mảng đặc chứa số hữu hạn. Đây là chính sách minh họa, không phải quy tắc bắt buộc cho mọi sản phẩm.

function sortedQuantities(values) {
  if (!Array.isArray(values) ||
      !Array.from(values).every(Number.isFinite)) {
    throw new TypeError('Expected an array of finite numbers');
  }
  return [...values].sort((a, b) => a - b);
}

Array.from làm các vị trí trống trở thành undefined để phép kiểm tra không bỏ qua chúng. Chuỗi "25", null, NaN và Infinity bị từ chối. Nếu API gửi số dưới dạng chuỗi, cần bước chuyển đổi có quy tắc rõ ràng trước đó; đừng âm thầm coi chuỗi rỗng là số 0.

4. Sao chép mảng không phải sao chép sâu

Với mảng object, [...items] tạo mảng mới nhưng các phần tử vẫn tham chiếu đến object cũ. Sắp xếp bản sao không đổi thứ tự của mảng nguồn, nhưng sửa một thuộc tính trên object vẫn có thể ảnh hưởng dữ liệu dùng chung.

Ví dụ danh sách sản phẩm có thể sắp theo số lượng rồi theo ID khi bằng nhau. Hãy xác định tiêu chí phụ nếu giao diện cần thứ tự cố định giữa các lần lấy dữ liệu; không dùng vị trí ngẫu nhiên từ API như một quy tắc nghiệp vụ ngầm.

5. Checklist kiểm thử

  • Dữ liệu có số một chữ số và nhiều chữ số để phát hiện sắp xếp kiểu chuỗi.
  • Số âm, số 0 và giá trị trùng được xử lý đúng.
  • Mảng nguồn giữ nguyên khi yêu cầu không được thay đổi dữ liệu đầu vào.
  • Chuỗi số và dữ liệu thiếu có chính sách riêng.
  • Mảng rỗng trả về mảng rỗng.
  • Với object, comparator không sửa thuộc tính của phần tử.

Sắp xếp đúng bắt đầu từ kiểu dữ liệu và quy tắc thứ tự. Một dòng comparator ngắn chỉ đáng tin khi đầu vào, hành vi sao chép và các trường hợp biên đã được xác định.

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.