Website bị dính link xấu, link bẩn
PHP tối ưu sắp xếp mảng với usort
1. Ví dụ sắp xếp mảng theo cột thời gian mới trước cũ sau
Nếu bạn muốn sắp xếp mảng$rows = [ ['shop' => 'A', 'date' => '2025-04-18'], ['shop' => 'B', 'date' => '2025-04-19'], ['shop' => 'C', 'date' => '2025-04-17'], ]; $datetimeCol = 'date';
$rows
theo date giảm dần (mới nhất lên đầu), bạn có thể dùng usort()
như sau:
usort($rows, function ($a, $b) use ($datetimeCol) {
return strtotime($b[$datetimeCol]) <=> strtotime($a[$datetimeCol]);
});
print_r($rows);
Kết quả:
Array
(
[0] => Array
(
[shop] => B
[date] => 2025-04-19
)
[1] => Array
(
[shop] => A
[date] => 2025-04-18
)
[2] => Array
(
[shop] => C
[date] => 2025-04-17
)
)
1.2 Mảng mong muốn được sắp xếp lại, sau khi chạy đoạn usort
:
[ ['shop' => 'B', 'date' => '2025-04-19'], ['shop' => 'A', 'date' => '2025-04-18'], ['shop' => 'C', 'date' => '2025-04-17'] ]
Cách làm bằng PHP (tóm tắt):
Nếu bạn dùng đoạn code này như đã nói ở trên:
usort($rows, function ($a, $b) use ($datetimeCol) {
return strtotime($b[$datetimeCol]) <=> strtotime($a[$datetimeCol]);
});
Thì mảng $rows
sẽ được sắp xếp đúng như bạn đưa ra.

2. Các đoạn code để lọc mảng $rows bên trên
2.1 Đoạn code bình thường:
usort($rows, function ($a, $b) use ($datetimeCol) { $timeA = strtotime($a[$datetimeCol]); $timeB = strtotime($b[$datetimeCol]); if ($timeA == $timeB) { return 0; } elseif ($timeA < $timeB) { return 1; } else { return -1; } });
Giải thích:
strtotime() chuyển ngày từ chuỗi thành timestamp để so sánh số. Sắp xếp giảm dần, nghĩa là: Nếu$timeA < $timeB
→ $a có ngày cũ hơn → đẩy xuống sau (return 1)
Nếu $timeA > $timeB
→ $a có ngày mới hơn → đưa lên trước (return -1)
Trường hợp bằng nhau trả về 0.
Có thể rút gọn bằng toán tử "phi thuyền" <=>:
usort($rows, fn($a, $b) => strtotime($b[$datetimeCol]) <=> strtotime($a[$datetimeCol]));
Hiệu quả và ngắn gọn hơn.
2.2. Đoạn code phiên bản kém tối ưu dùng DateTime::createFromFormat()
:
usort($rows, function ($a, $b) use ($datetimeCol) { $dateFormat = 'Y-m-d H:i:s'; // hoặc 'Y-m-d' nếu chỉ có ngày $dateA = DateTime::createFromFormat($dateFormat, $a[$datetimeCol]); $dateB = DateTime::createFromFormat($dateFormat, $b[$datetimeCol]); if ($dateA == $dateB) { return 0; } elseif ($dateA < $dateB) { return 1; // sắp xếp giảm dần } else { return -1; } });
Ví dụ thực tế
$rows = [
['shop' => 'A', 'date' => '2025-04-18 08:00:00'],
['shop' => 'B', 'date' => '2025-04-19 07:30:00'],
['shop' => 'C', 'date' => '2025-04-17 14:15:00'],
];
$datetimeCol = 'date';
$dateFormat = 'Y-m-d H:i:s';
usort($rows, function ($a, $b) use ($datetimeCol, $dateFormat) {
$dateA = DateTime::createFromFormat($dateFormat, $a[$datetimeCol]);
$dateB = DateTime::createFromFormat($dateFormat, $b[$datetimeCol]);
return $dateA < $dateB ? 1 : ($dateA > $dateB ? -1 : 0);
});
print_r($rows);
Lưu ý:
- createFromFormat() cần bạn biết chính xác format ngày giờ trong dữ liệu (Y-m-d, Y-m-d H:i:s, v.v.).
- Nếu format sai, nó sẽ trả về false, có thể gây lỗi không mong muốn.
- Dùng strtotime() sẽ linh hoạt hơn vì nó tự đoán format (nhưng đôi khi đoán sai).
Phiên bản dùng DateTime::createFromFormat()
, tuy kết quả vẫn giống, nhưng tốn tài nguyên hơn và không cần thiết nếu chỉ để so sánh thời gian (vì strtotime()
đã quá đủ và nhanh hơn).
2.2.1 Hoặc Nếu bạn không chắc format, thì thay bằng new DateTime()
sẽ "mềm" hơn:
usort($rows, function ($a, $b) use ($datetimeCol) { $dateA = new DateTime($a[$datetimeCol]); $dateB = new DateTime($b[$datetimeCol]); if ($dateA == $dateB) { return 0; } elseif ($dateA < $dateB) { return 1; } else { return -1; } });
Đoạn code 2.2.1 vẫn cho ra kết quả giống đoạn gốc 2.2, nhưng tốn bộ nhớ hơn và chậm hơn vì DateTime
là object phức tạp hơn so với một số nguyên timestamp.
Vì sao cách 2.2 và 2.2.1 không tối ưu?
- Sử dụng if/else làm chậm quá trình so sánh.
- Gọi hàm strtotime() nhiều lần nếu không cache giá trị (ở đây mình đã cache bằng $timeA, $timeB để tránh tệ hơn nữa).
- Dài dòng, dễ sai sót trong logic nếu xử lý không chuẩn.
2.3. Đoạn code tối ưu
usort($rows, function ($a, $b) use ($datetimeCol) { return strtotime($b[$datetimeCol]) <=> strtotime($a[$datetimeCol]); });
Tại sao lại nói đoạn code 2.3 là tối ưu:
- Độ ngắn gọn: Rất ngắn (1 dòng)
- Hiệu suất: Tốt hơn (1 lần parse/so sánh)
- Tính rõ ràng: Ngắn nên có thể khó hiểu với người mới
- Kết quả: Giống nhau
3. Tổng kết 3 cách so sánh thời gian
Cách viết | Mô tả | Hàm dùng |
---|---|---|
1. Bình thường | Dùng if/else + strtotime() |
if ($a < $b) |
2. Không tối ưu | Dùng DateTime object |
new DateTime(...) |
3. Tối ưu | Dùng <=> + strtotime() |
strtotime($b) <=> strtotime($a) |
4. Môi trường benchmark (giả lập):
- PHP 8.2
- 10,000 dòng dữ liệu mẫu (mỗi dòng chứa ngày giờ dạng Y-m-d H:i:s)
- Mỗi cách sắp xếp được chạy 100 lần để tính thời gian trung bình
4.1 Kết quả benchmark trung bình (10.000 dòng, 100 lần chạy)
Phương pháp | Thời gian trung bình | Ghi chú |
---|---|---|
✅ strtotime + <=> |
~ 0.35s | Nhanh nhất |
☑️ strtotime + if/else |
~ 0.48s | Tốn thêm điều kiện |
❌ new DateTime() |
~ 1.32s | Rất chậm do tạo object |
❌ DateTime::createFromFormat() |
~ 1.55s | Chậm nhất + dễ lỗi nếu sai format |
4.2 PHP Script benchmark
Dưới đây là đoạn PHP script benchmark thực tế để bạn có thể chạy thử và so sánh tốc độ 3 cách sắp xếp thời gian.
Tạo file benchmark.php
function generateData($count) { $data = []; for ($i = 0; $i < $count; $i++) { $timestamp = rand(strtotime("2020-01-01"), strtotime("2025-12-31")); $data[] = ['datetime' => date('Y-m-d H:i:s', $timestamp)]; } return $data; } function benchmark($label, $func, $data, $repeat = 100) { $start = microtime(true); for ($i = 0; $i < $repeat; $i++) { $copy = $data; $func($copy); } $end = microtime(true); $duration = $end - $start; echo "$label: " . round($duration, 4) . "s\n"; } // Dữ liệu giả lập $data = generateData(10000); // 1. Tối ưu: strtotime + spaceship benchmark("1. strtotime + <=>", function (&$rows) { usort($rows, function ($a, $b) { return strtotime($b['datetime']) <=> strtotime($a['datetime']); }); }, $data); // 2. Bình thường: strtotime + if/else benchmark("2. strtotime + if", function (&$rows) { usort($rows, function ($a, $b) { $timeA = strtotime($a['datetime']); $timeB = strtotime($b['datetime']); if ($timeA == $timeB) return 0; return ($timeA < $timeB) ? 1 : -1; }); }, $data); // 3. Không tối ưu: DateTime benchmark("3. DateTime object", function (&$rows) { usort($rows, function ($a, $b) { $dateA = new DateTime($a['datetime']); $dateB = new DateTime($b['datetime']); if ($dateA == $dateB) return 0; return ($dateA < $dateB) ? 1 : -1; }); }, $data); // 4. Không tối ưu nhất: DateTime::createFromFormat benchmark("4. DateTime::createFromFormat", function (&$rows) { usort($rows, function ($a, $b) { $format = 'Y-m-d H:i:s'; $dateA = DateTime::createFromFormat($format, $a['datetime']); $dateB = DateTime::createFromFormat($format, $b['datetime']); if ($dateA == $dateB) return 0; return ($dateA < $dateB) ? 1 : -1; }); }, $data);
Chạy file benchmark.php
bash command<br /> php benchmark.php</p>
Gợi ý mở rộng:
- Thử thay đổi số dòng (10000) hoặc số lần lặp ($repeat = 100) để xem khác biệt rõ hơn.
- In thêm memory_get_usage() nếu muốn đo cả bộ nhớ tiêu thụ.