Sorting 2-D associative array and categorizing on basis of a key value
Asked Answered
H

4

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.

Homoio answered 23/4, 2013 at 1:55 Comment(0)
A
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);
Azerbaijan answered 23/4, 2013 at 2:51 Comment(0)
F
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://mcmap.net/q/46291/-how-can-i-sort-arrays-and-data-in-php

Fisken answered 23/4, 2013 at 2:41 Comment(0)
P
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);
}
Pile answered 23/4, 2013 at 2:49 Comment(0)
L
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 value is not in the priority array, fallback to PHP_INT_MAX to give that value the lowest possible priority. All values that are not mentioned in the lookup array will maintain their relative position amongst the unmentioned group of values.

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']);

usort(
    $array,
    fn($a, $b) => ($lookup[$a['duty']] ?? PHP_INT_MAX) <=> ($lookup[$b['duty']] ?? PHP_INT_MAX)
);
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 that order of low-priority values is "Last name" ASC, "First name" ASC, then "Prefix" ASC.

Code: (Demo)

usort(
    $array,
    fn($a, $b) =>
        ($lookup[$a['duty']] ?? PHP_INT_MAX) <=> ($lookup[$b['duty']] ?? PHP_INT_MAX)
        ?: array_reverse(preg_match('~^((?:[a-z]+\.)?) ?(\S+) (.+)~i', $a['name'], $out) ? $out : ['', '', ''])
           <=>
           array_reverse(preg_match('~^((?:[a-z]+\.)?) ?(\S+) (.+)~i', $b['name'], $out) ? $out : ['', '', ''])
);
var_export($array);
Lenity answered 11/9, 2020 at 8:13 Comment(0)
A
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);
Azerbaijan answered 23/4, 2013 at 2:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.