0

I'm a newbie here. Have a question about a php sorting thing. Have searched a lot, but unfortunately couldn't find a solution to what I was searching. I know it's easy -- but I don't know why I couldn't find it after searching for hours.

I've an array like this. They have a key "duty" , I want to do some kind of Sorting on this so that the new array contains - arrays inorder with the "duty" field.

$arrayName = array(
array(
    'name' => 'Dr. Hugo Lopez',
    'duty' => 'Anesthesiologist',
    'link' => ''
),
array(
    'name' => 'Dr. Dario Garin',
    'duty' => 'Orthopedic Specialist',
    'link' => 'dr-dario-garin.php'
),
array(
    'name' => 'Dr. Maclovio Yañez',
    'duty' => 'Plastic Surgeon',
    'link' => ''
),
array(
    'name' => 'Melissa Bracker',
    'duty' => 'Patient Liaison',
    'link' => ''
),
array(
    'name' => 'Dr. Diego Guzman',
    'duty' => 'Cardiologist',
    'link' => ''
),
array(
    'name' => 'Ivan Arafat',
    'duty' => 'Accountant',
    'link' => ''
),
array(
    'img-old' => 'jorge-fernandez.jpg',
    'duty' => 'Hospital Administrator',
    'link' => ''
),
);

I tried:

usort($arrayName, function($a, $b) {
return $a['duty'] - $b['duty'];
});

But this arranges the array in descending order of the Duty field.

I want to arrange it in order with a priority on Duty field. Like : I will set the order of Duty field values to arrange them in categories of Nurse, Maintenance, Doctors, Drivers and etc.

I want to be able to - set the order of categorization by myself using some sort of array.

sort($arrayName, $priority);
$priority = array( 'President', 'Vice President', 'Doctors', 'Nurse', 'Maintenance', 'Drivers');

I'm not much familiar to PHP. Pardon if I am asking basic questions.

Jared
  • 25,627
  • 7
  • 56
  • 61
OmAk
  • 3
  • 3

4 Answers4

2
usort($arrayName, function (array $a, array $b) {
    static $priority = array('President', 'Vice President', 'Doctors', 'Nurse', 'Maintenance', 'Drivers');
    return array_search($a['duty'], $priority) - array_search($b['duty'], $priority);
});

Extensive explanation here: https://stackoverflow.com/a/17364128/476

deceze
  • 510,633
  • 85
  • 743
  • 889
1

Honestly I would change the way you have organized your data structure- there are many minor inefficiencies that might hurt you down the road. It's more natural for your list of professionals to belong TO a department rather than have an attribute that describes their role. If you group your users into the following data structure, it becomes much easier to perform sorting and other 'department' level tasks while not making 'user' level tasks much more difficult at all.

$arry = array(
   'Accountant' => array(
        ...
    ),
    'Anesthesiologist' => array(
        ...
    ),
)

If that's not an option.. you can use the function below.. but please note that I wouldn't recommend sorting the data this way in any production scenario. It iterates the entire user list for each priority that you set which is quite an inefficient sorting method. Imagine a scenario where you have 100 priorities and 1000 users- This would take 100 * 1000 = 100,000 iterations to simply prioritize data. The worst part is that it gets exponentially slower.

Anyway, here is the inefficient solution..

$priorities = array(
        'Accountant',
        'Cardiologist',
        'Anesthesiologist',
);

$arrayName = array(
...
);

$r = sortDuties($arrayName, $priorities);
var_dump($r);

function sortDuties($userList, $priorities = array()) {
 $newList = array();
 foreach($priorities AS $priority) {
    for($i = 0; $i < count($userList); $i++) {
        if(isset($userList[$i]) && $userList[$i]['duty'] == $priority){
            $newList[] = $userList[$i];
            unset($userList[$i]);
        }
    }
 }

 return array_merge($newList, $userList);
}
Kevin
  • 523
  • 4
  • 20
1

Because isset() (or null coalescing as I'll demonstrate) always outperforms array_search(), I recommend that you prepare a lookup array by flipping your priority array. When a duty is not in the priority array, you will need to assign a number that is higher than your highest priority value -- to do this, just use count().

Code: (Demo)

$array = [
    ['name' => 'Dr. Hugo Lopez', 'duty' => 'Anesthesiologist', 'link' => ''],
    ['name' => 'Dr. Dario Garin', 'duty' => 'Orthopedic Specialist', 'link' => 'dr-dario-garin.php'],
    ['name' => 'Dr. Maclovio Yañez', 'duty' => 'Plastic Surgeon', 'link' => ''],
    ['name' => 'Melissa Bracker', 'duty' => 'Patient Liaison', 'link' => ''],
    ['name' => 'Dr. Diego Guzman', 'duty' => 'Cardiologist', 'link' => ''],
    ['name' => 'Ivan Arafat', 'duty' => 'Accountant', 'link' => ''],
    ['name' => 'Jorge Fernandez', 'duty' => 'Hospital Administrator', 'link' => ''],
];

$lookup = array_flip(['Hospital Administrator', 'Plastic Surgeon', 'Orthopedic Specialist']);
$fallback = count($lookup);

usort($array, function ($a, $b) use ($lookup, $fallback) {
    return ($lookup[$a['duty']] ?? $fallback) <=> ($lookup[$b['duty']] ?? $fallback);
});
var_export($array);

If you want to extend the sorting rules based on name, the spaceship operator makes that very simple. Truth is, the parsing of the name strings is harder than the sorting. I'll split the names into a max of 3 elements and then reverse their order so that it goes "Last name", "First name", "Prefix".

Code: (Demo)

usort($array, function ($a, $b) use ($lookup, $fallback) {
    $aName = array_reverse(preg_match('~^((?:[a-z]+\.)?) ?(\S+) (.+)~i', $a['name'], $out) ? $out : ['', '', '']);
    $bName = array_reverse(preg_match('~^((?:[a-z]+\.)?) ?(\S+) (.+)~i', $b['name'], $out) ? $out : ['', '', '']);
    return [$lookup[$a['duty']] ?? $fallback, ...$aName]
           <=>
           [$lookup[$b['duty']] ?? $fallback, ...$bName];
});
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
0

Here is my solution, not the most efficient but it should do the trick, and it should account for the issue of a duty not defined as a priority.

$priority = array('Orthopedic Specialist', 'Hospital Administrator', 'Accountant', 'Anesthesiologist', 'Plastic Surgeon');

function dutySort(array &$array, array $priority) {

    usort($array, function($a, $b) use ($priority) {
        $aPriority = array_search($a['duty'], $priority);
        $aPriority = ($aPriority !== false ? $aPriority : count($priority));
        $bPriority = array_search($b['duty'], $priority);
        $bPriority = ($bPriority !== false ? $bPriority : count($priority));

        return ($aPriority < $bPriority) ? -1 : 1;
    });
}

dutySort($array, $priority);
maxiscool
  • 537
  • 3
  • 9