usort multisort array based on another array
Asked Answered
F

4

0

I have array that I need to sort by another array form 2 fields zip code and approve

I am able to sort it by zip code but unable to do it with approved field so for eg

I need to sort by 60007,60001,60003,60002 (as per sortlike order) all zip code from 60007 and approved should come first so

$sortLike=array(60007,60001,60003,60002);
$array1= array(
    array ('ID' => 138,'zip_code' => 60007,'approved' => 1),
    array('ID' => 103,'zip_code' => 60007,'approved' => 0),
    array('ID' => 114,'zip_code' => 60007,'approved' => 1),
    array('ID' => 105,'zip_code' => 60003,'approved' => 0),
    array('ID' => 124,'zip_code' => 60002,'approved' => 0)
)

so 60007 and aproved should come first than 60007 with 0(unapproved) and than all 60001 approved than all 60001 unapproved(and so on as per $sortlike) here is complete php code

<?php
$array1= array(
    array ('ID' => 138,'zip_code' => 60007,'approved' => 1),
    array('ID' => 103,'zip_code' => 60007,'approved' => 0),
    array('ID' => 114,'zip_code' => 60007,'approved' => 1),
    array('ID' => 105,'zip_code' => 60003,'approved' => 0),
    array('ID' => 124,'zip_code' => 60002,'approved' => 0),
    array('ID' => 104,'zip_code' => 60002,'approved' => 1),
    array('ID' => 106,'zip_code' => 60001,'approved' => 0),
    array('ID' => 188,'zip_code' => 60022,'approved' => 0),
    array('ID' => 184,'zip_code' => 60022,'approved' => 1),
);
function sort_results ($a, $b) {
   $sortLike=array(60007,60001,60003,60002);
   if (!in_array ($a['zip_code'], $sortLike)) { return 1; } // Push unknown values at the end of the array
   return array_search($a['zip_code'], $sortLike) > array_search($b['zip_code'], $sortLike);
}

usort ($array1, 'sort_results');
echo "<pre>";
print_r($array1);
echo "</pre>";
Flews answered 1/9, 2014 at 10:1 Comment(0)
G
1

I guess you're asking how you can also sort by 'approved', because that is currently missing in your code.

The solution to this question is to define an order in which the keys of your input array $array1 are checked.

So you need to check zip_code at first. If you get a result != 0, you can return immediately. But if the result is == 0 (the zip codes are equal) you need to check the second key ('approved' in your case).

$array1= array(
    array ('ID' => 138,'zip_code' => 60007,'approved' => 1),
    array('ID' => 103,'zip_code' => 60007,'approved' => 0),
    array('ID' => 114,'zip_code' => 60007,'approved' => 1),
    array('ID' => 105,'zip_code' => 60003,'approved' => 0),
    array('ID' => 124,'zip_code' => 60002,'approved' => 0),
    array('ID' => 104,'zip_code' => 60002,'approved' => 1),
    array('ID' => 106,'zip_code' => 60001,'approved' => 0),
    array('ID' => 188,'zip_code' => 60022,'approved' => 0),
    array('ID' => 184,'zip_code' => 60022,'approved' => 1),
);
function sort_results ($a, $b) {
    $sortLike=array(60007,60001,60003,60002);
    if (!in_array ($a['zip_code'], $sortLike)) { return 1; } // Push unknown values at the end of the array
    $zip_res = array_search($a['zip_code'], $sortLike) - array_search($b['zip_code'], $sortLike); // check zip_code order 
    if($zip_res == 0){ // if the zip_codes are equal
        $zip_res = $b['approved'] - $a['approved']; // sort by the 'approved' key
    }
    return $zip_res;
}

//edit

You can use a lamda function to move $sortLike out of the function like this (requires PHP >= 5.3):

$sortLike=array(60007,60001,60003,60002);
$sort_results = function ($a, $b) use ($sortLike){ // create lambda function with "use" keyword
    if (!in_array ($a['zip_code'], $sortLike)) { return 1; } // Push unknown values at the end of the array
    $zip_res = array_search($a['zip_code'], $sortLike) - array_search($b['zip_code'], $sortLike);
    if($zip_res == 0){
        $zip_res = $b['approved'] - $a['approved'];
    }
    return $zip_res;
};

usort ($array1,$sort_results); // pass lambda function in
echo "<pre>";
print_r($array1);
echo "</pre>";
die();
Guarani answered 1/9, 2014 at 10:29 Comment(2)
This is what I need and seems very faster as compared to other solutions, Can you help putting $sortLike outside the sort_results as sort_result is an array that comes from database , so $sortLike has to be passed as an argument to call to usortFlews
Sure, you can use an anonymous function to do that easily. I update the answer to include the corresponding code. Feel free to accept the answer if it solved your problem ;)Guarani
L
1
usort($array_data, array($sort_obj = new cmpArray('array_key'), "cmp__"));
class cmpArray
     {

         var $key;

         function __construct($key)
         {
             $this->key = $key;
         }

         function cmp__($a, $b)
         {
             $key = $this->key;
             if($a[$key] == $b[$key])
                 return 0;

             return (($a[$key] > $b[$key]) ? 1 : -1);
         }

     }
Loveless answered 1/9, 2014 at 10:21 Comment(2)
in place of array_key you write your array key on which you want to sort your arrayLoveless
I am looking to sort on 2 array elements 1) zip code 2) approved fieldFlews
G
1

I guess you're asking how you can also sort by 'approved', because that is currently missing in your code.

The solution to this question is to define an order in which the keys of your input array $array1 are checked.

So you need to check zip_code at first. If you get a result != 0, you can return immediately. But if the result is == 0 (the zip codes are equal) you need to check the second key ('approved' in your case).

$array1= array(
    array ('ID' => 138,'zip_code' => 60007,'approved' => 1),
    array('ID' => 103,'zip_code' => 60007,'approved' => 0),
    array('ID' => 114,'zip_code' => 60007,'approved' => 1),
    array('ID' => 105,'zip_code' => 60003,'approved' => 0),
    array('ID' => 124,'zip_code' => 60002,'approved' => 0),
    array('ID' => 104,'zip_code' => 60002,'approved' => 1),
    array('ID' => 106,'zip_code' => 60001,'approved' => 0),
    array('ID' => 188,'zip_code' => 60022,'approved' => 0),
    array('ID' => 184,'zip_code' => 60022,'approved' => 1),
);
function sort_results ($a, $b) {
    $sortLike=array(60007,60001,60003,60002);
    if (!in_array ($a['zip_code'], $sortLike)) { return 1; } // Push unknown values at the end of the array
    $zip_res = array_search($a['zip_code'], $sortLike) - array_search($b['zip_code'], $sortLike); // check zip_code order 
    if($zip_res == 0){ // if the zip_codes are equal
        $zip_res = $b['approved'] - $a['approved']; // sort by the 'approved' key
    }
    return $zip_res;
}

//edit

You can use a lamda function to move $sortLike out of the function like this (requires PHP >= 5.3):

$sortLike=array(60007,60001,60003,60002);
$sort_results = function ($a, $b) use ($sortLike){ // create lambda function with "use" keyword
    if (!in_array ($a['zip_code'], $sortLike)) { return 1; } // Push unknown values at the end of the array
    $zip_res = array_search($a['zip_code'], $sortLike) - array_search($b['zip_code'], $sortLike);
    if($zip_res == 0){
        $zip_res = $b['approved'] - $a['approved'];
    }
    return $zip_res;
};

usort ($array1,$sort_results); // pass lambda function in
echo "<pre>";
print_r($array1);
echo "</pre>";
die();
Guarani answered 1/9, 2014 at 10:29 Comment(2)
This is what I need and seems very faster as compared to other solutions, Can you help putting $sortLike outside the sort_results as sort_result is an array that comes from database , so $sortLike has to be passed as an argument to call to usortFlews
Sure, you can use an anonymous function to do that easily. I update the answer to include the corresponding code. Feel free to accept the answer if it solved your problem ;)Guarani
F
0
<?php
$array1= array(
array ('ID' => 138,'zip_code' => 60007,'approved' => 1),
array('ID' => 103,'zip_code' => 60007,'approved' => 0),
array('ID' => 114,'zip_code' => 60007,'approved' => 1),
array('ID' => 105,'zip_code' => 60003,'approved' => 0),
array('ID' => 124,'zip_code' => 60002,'approved' => 0),
array('ID' => 104,'zip_code' => 60002,'approved' => 1),
array('ID' => 106,'zip_code' => 60001,'approved' => 0),
array('ID' => 188,'zip_code' => 60022,'approved' => 0),
array('ID' => 184,'zip_code' => 60022,'approved' => 1),
);

function sort_it ($a) {
$sortedarr = array();
$sortLike=array(60007,60001,60003,60002);
foreach($sortLike as $el){
    array_push($sortedarr,getElements($a,$el));
}
}



function getElements($arr,$e){
$newarr = array();
foreach($arr as $val){
    if($val['approved'] == '1' && $val['zip_code'] == $e){
    $newarr[] = $val;
    }
}
foreach($arr as $val){
    if($val['approved'] == '0' && $val['zip_code'] == $e){
    $newarr[] = $val;
    }
}
return $newarr;
}

$array2 = sort_it($array1);
echo "<pre>";
print_r($array1);
echo "</pre>";
echo "<pre>";
print_r($array2);
echo "</pre>";
?>
Funkhouser answered 1/9, 2014 at 10:18 Comment(0)
P
0

usort() PHP < v7.4: (Demo)

$priorityZips = array_flip([60007, 60001, 60003, 60002]);
$priorityCount = count($priorityZips);

usort($rows, function($a, $b) use ($priorityZips, $priorityCount) {
    return [
               $priorityZips[$a['zip_code']] ?? $priorityCount,
               $a['zip_code'],
               $b['approved']
           ]
           <=>
           [
               $priorityZips[$b['zip_code']] ?? $priorityCount,
               $b['zip_code'],
               $a['approved']
           ];
});

usort() PHP >= v7.4: (Demo)

$priorityZips = array_flip([60007, 60001, 60003, 60002]);
$priorityCount = count($priorityZips);

usort($rows, fn($a, $b) =>
    [
        $priorityZips[$a['zip_code']] ?? $priorityCount,
        $a['zip_code'],
        $b['approved']
    ]
    <=>
    [
        $priorityZips[$b['zip_code']] ?? $priorityCount,
        $b['zip_code'],
        $a['approved']
    ]
);

array_multisort() PHP < v7.4: (Demo)

$priorityZips = array_flip([60007, 60001, 60003, 60002]);
$priorityCount = count($priorityZips);

array_multisort(
    array_map(
        function($row) use ($priorityZips, $priorityCount) {
            return $priorityZips[$row['zip_code']] ?? $priorityCount;
        },
        $rows
    ),
    array_column($rows, 'zip_code'),
    array_column($rows, 'approved'),
    SORT_DESC,
    $rows
);

array_multisort() PHP >= v7.4: (Demo)

$priorityZips = array_flip([60007, 60001, 60003, 60002]);
$priorityCount = count($priorityZips);

array_multisort(
    array_map(
        fn($row) => $priorityZips[$row['zip_code']] ?? $priorityCount,
        $rows
    ),
    array_column($rows, 'zip_code'),
    array_column($rows, 'approved'),
    SORT_DESC,
    $rows
);

In my snippets above, I refer to your input array to be sorted as $rows.

This task can be performed several ways, but I've shown a few that I feel are cleanest / most readable. If performance is critical in your application, I'll encourage you to benchmark using your own real data; otherwise choose the snippet that you are most comfortable maintaining long-term.

In PHP versions below 7.4, passing variables into custom function scopes is done using use(). In PHP7.4 and higher, "arrow function syntax", strips away a fair chunk of syntax in the custom function and allows the entry of variables without use().

The "spaceship operator" / "three-way comparison operator" (<=>) makes building multi-criteria sorting MUCH cleaner and concise versus a bunch of condition blocks. The spaceship operator will synchronously process the elements in the arrays and return the correctly evaluated -1, 0, or 1. It will also treat numeric strings as numbers.

I flipped the lookup array of prioritized zipcodes to enable the "null coalescing operator" (??) to swiftly return the priority value or else default to a number greater than the highest integer value in the lookup.

Regarding the structure of the usort arrays being compared, you may like to see this answer that goes into a bit more detail.

Portsalut answered 9/4, 2020 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.