For best efficiency, iterate the input array just once and populate a new array (of equal length) by parsing each string, looking up the unit factor, then pushing the product of the multiplied integers into the new array.
Using the newly populated array and the original array to array_multisort()
will not preserve the original keys.
Parsing the string with sscanf()
is very convenient because you can instantly cast substrings as integers or floats (which explode()
, preg_match()
, etc. cannot do). Also, sscanf()
will elegantly ignore the potential space between the amount and the unit.
Code: (Demo) (Same technique with grams)
const UNIT_FACTOR = [
'B' => 1,
'KB' => 1024,
'MB' => 1048576,
'GB' => 1073741824,
];
$array = [
0 => "100 MB",
4 => "10 MB",
8 => "1GB",
12 => "20 MB",
16 => "250 MB",
20 => "2GB",
24 => "4 MB",
28 => "500 MB",
32 => "50 MB",
36 => "5GB",
40 => "8GB",
44 => "0 MB"
];
$bytes = [];
foreach ($array as $string) {
sscanf($string, '%f%s', $amount, $unit);
$bytes[] = $amount * UNIT_FACTOR[$unit];
}
array_multisort($bytes, $array);
var_export($array);
Output:
array (
0 => '0 MB',
1 => '4 MB',
2 => '10 MB',
3 => '20 MB',
4 => '50 MB',
5 => '100 MB',
6 => '250 MB',
7 => '500 MB',
8 => '1GB',
9 => '2GB',
10 => '5GB',
11 => '8GB',
)
A less efficient approach that preserves array keys would be uasort()
.
Code: (Demo)
const UNIT_FACTOR = [
'B' => 1,
'KB' => 1024,
'MB' => 1048576,
'GB' => 1073741824,
];
$array = [
0 => "100 MB",
4 => "10 MB",
8 => "1GB",
12 => "20 MB",
16 => "250 MB",
20 => "2GB",
24 => "4 MB",
28 => "500 MB",
32 => "50 MB",
36 => "5GB",
40 => "8GB",
44 => "0 MB"
];
uasort(
$array,
function($a, $b) {
sscanf($a, '%f %s', $amountA, $unitA);
sscanf($b, '%f %s', $amountB, $unitB);
return $amountA * UNIT_FACTOR[$unitA]
<=> $amountB * UNIT_FACTOR[$unitB];
}
);
var_export($array);
However, the number of iterated function calls can be reduced by establishing a lookup array of calculated byte values before calling uasort()
.
Code: (Demo)
$bytes = [];
foreach ($array as $string) {
sscanf($string, '%f %s', $amount, $unit);
$bytes[$string] = $amount * UNIT_FACTOR[$unit];
}
uasort($array, fn($a, $b) => $bytes[$a] <=> $bytes[$b]);
var_export($array);