Reputation: 158
I have an array of values that I'd like to sort alphabetically, but with numbers sorted after characters.
For instance, given the following strings:
$array = [
'Core 2 Duo',
'Core Duo',
'Core Quad',
'Core 4 Quad'
];
Desired result:
[
'Core Duo',
'Core 2 Duo',
'Core Quad',
'Core 4 Quad'
]
PHP's sort() function is not suitable because it puts the mid-string numbers first, like this:
[
'Core 2 Duo',
'Core 4 Quad',
'Core Duo',
'Core Quad'
]
Natural sorting returns the same results as regular sorting.
Is there any way to tell PHP something like, "If you encounter a number, sort it as if it comes at the end of the sting"?
Upvotes: 2
Views: 1285
Reputation: 47900
For reliability, use preg_replace()
to move all of the numeric substrings to the back of the string, then use natural sorting inside of a array_multisort()
call. (Demo)
$array = [
'Core 2 Duo',
'Core Duo',
'Core Quad',
'Core 12 Duo',
'Core 11 Duo',
'Core 102 Duo',
'Core 1 Duo',
'Core 2 Quad'
];
array_multisort(
preg_replace('/( \d+)(.*)/', '$2$1', $array),
SORT_NATURAL,
$array
);
var_export($array);
Output:
array (
0 => 'Core Duo',
1 => 'Core 1 Duo',
2 => 'Core 2 Duo',
3 => 'Core 11 Duo',
4 => 'Core 12 Duo',
5 => 'Core 102 Duo',
6 => 'Core Quad',
7 => 'Core 2 Quad',
)
Upvotes: 0
Reputation: 14523
Here is your solution:
<?php
$array = array('Core 2 Duo', 'Core Duo', 'Core Quad', 'Core 4 Quad');
$tempArray = array();
foreach($array as $key=>$value) {
$exp = explode(" ",$value);
if(count($exp)==3) {
$tempArray[$key] = $exp[0]." ".$exp[2]." ".$exp[1];
} else {
$tempArray[$key] = $exp[0]." ".$exp[1]." "."-1";
}
}
asort($tempArray);
$sortedArray = array();
foreach($tempArray as $key=>$value) {
$sortedArray[] = $array[$key];
}
print_r($sortedArray);
?>
Output:
Array
(
[0] => Core Duo
[1] => Core 2 Duo
[2] => Core Quad
[3] => Core 4 Quad
)
Upvotes: 1
Reputation: 173572
It seems that what you're after is a function that pushes all numeric elements of your strings to the back, e.g. "Core 2 Duo" => "Core Duo 2".
// moves numeric parts of the string to the back
function transform($s)
{
$parts = explode(' ', $s);
foreach (array_filter($parts, 'is_numeric') as $key => $value) {
unset($parts[$key]);
$parts[] = $value;
}
return join(' ', $parts);
}
// natural sort on transformed strings
function cmp($a, $b)
{
return strnatcmp(transform($a), transform($b));
}
$a = array('Core 2 Duo', 'Core Duo', 'Core Quad', 'Core 2 Quad');
usort($a, 'cmp');
print_r($a);
Upvotes: 3